Skip to content

πŸ› fix(holdguard): one branch update per head and base; stop and say why - #14401

Merged
hivecommons-hive[bot] merged 3 commits into
hivecommons:v6from
Danathar:fix/hold-guard-update-retry
Oct 27, 2026
Merged

hivecommons-hive[bot] merged 3 commits into
hivecommons:v6from
Danathar:fix/hold-guard-update-retry

Conversation

@Danathar

@Danathar Danathar commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The hold guard keeps held PRs mergeable by merging their base in (#10437). On every hive running the default convergence.mode (shadow), it could make that update once per PR and then never again. After that, it logged hold guard: could not confirm new head after keeping held PR mergeable every pass, and the branch stayed behind.

Cause

maintainHeldPRMergeability called UpdateBranch, whose mutation-journal identity is only repo + branch_update + PR number: no head, no base. Once the first update was journaled as applied, every later update for that PR replayed as "external mutation already applied; treating as idempotent success". GitHub was never called, so the head never moved, and the guard warned and tried again the next pass.

Live evidence (hive on a08007f, then fcf0e78):

Causes ruled out for these PRs

Both still get a stop here, because they are refusals the same token would get again.

Change

Journal identity

  • The guard now calls UpdateBranchExpectedHead with the PR's head and base SHA. Each (head, base) is its own journaled attempt, the same way PR follow-up and supersession already call it. The update is also pinned to the head the guard saw.
  • Held PRs now carry BaseSHA from enumeration, as actionable PRs already did.

Accepted (202) but not visible yet

Refusals

  • Refusals are classified with the automerge sweep's existing classifier, moved to pkg/github (ClassifyBranchUpdateError). The automerge sweep keeps using it through aliases, unchanged.
  • These stop attempts for that head and base until either moves:
    • workflow-permission
    • fork-not-editable: also decided up front, with no call, when "allow edits by maintainers" is off
    • conflict
    • up-to-date
  • The reason is stored on the record (UpdateStopped*) and logged with the head and base.
  • Transient failures (5xx, rate limit, a fenced journal entry) are still retried on the next pass.

Person-facing note

  • When the guard stops for workflow-permission, fork-not-editable or update-not-landed, it posts one comment per head and reason. The comment names the reason and says to use Update branch; for a fork, it says to ask the author.
  • A conflict keeps the existing conflict notice.
  • A stale behind that GitHub calls up to date gets no note.

Returned to contributor (072)

  • The guard no longer merges the base into a PR at stage/072-returned-to-contributor. That stage means the author's hive is expected to push to the branch. A merge commit pushed under them only makes their next push fail until they pull, and the PR cannot merge before they act anyway.
  • A PR the hive adopted (074) is still kept up to date.

Docs citation

client.go gains one line (BaseSHA), which moves RankActionableIssues down a line, so src/docs/scale-envelope.md:37 now cites client.go:4712-4722.

Legacy records

  • Records written before this change with WorkflowRefusedHeadSHA still stop on that head. The field is no longer written.

Testing

Tests run from src/ under /var/tmp, with -count=1:

  • go test ./pkg/holdguard/ ./pkg/github/... ./pkg/convergence/... ./cmd/hive/: all ok (holdguard, github, automerge, requestwatch, the convergence packages and cmd/hive).
  • go vet ./cmd/hive/ is clean, and gofmt is clean on the changed files.
  • src/scripts/check-changelog-fragment.sh v6, check-docs-citations.py and check-docs-links.py pass.
  • pkg/holdguard coverage is 99.6% on its own tests (the gate's floor is 90%; it was 75.3% when the new methods were only tested from cmd/hive).

New tests in cmd/hive/hold_guard_update_retry_test.go:

Test Covers
…AsksGitHubAgainAfterAJournaledUpdate The #474 cause, with the journal a hive runs by default (shadow mode). After a first update, a new head and base reaches GitHub again and advances the baseline.
…WaitsOnAnAcceptedUpdate A slow 202: no re-request inside the window. After the window, a not-landed stop with one note and no further calls. A moved base tries again.
…SettlesAnUpdateThatLandsLater The update lands between ticks: the request settles and the baseline is not advanced.
…StopsOnAForkItCannotPushTo Fork with maintainer edits off (no call), a 422 maintainer-edits refusal, and a 403 on a fork: each stops with one note.
…RetriesRefusedHeadOnlyAfterHeadOrBaseMoves A refused head is not asked again until main or the head moves. A 502 is retried each pass.
…HonoursLegacyWorkflowRefusal Old records still stop on their head.
…LeavesAReturnedPRAlone 072 is not updated.
TestEnforceHoldGuardSettledUpdateIsNotDriftAtLift An update that lands between ticks, then a human lift: no drift note when the hive saw the merge while held; a re-hold when the lift came first.

New tests in pkg/holdguard/update_state_test.go: an awaited update is matched on its own head and base, expires after UpdateConfirmWindow, and settles once the head moves without moving the baseline. AdvanceHygiene clears it. A stop holds for one head and base and survives a reload, and the legacy workflows record still stops. The note is per head and reason, and the comment names each reason and what to do.

Changed existing tests in hold_guard_mergeability_test.go:

  • Workflows refusal test: it expected no comment. It now expects one note naming the workflows permission and Update branch. It also checks that a moved base retries without a second note for the same head.
  • Conflict test: it retried the same head and base after a conflict. It now asserts that the same head and base are not asked again, and resolves the conflict through a base move.

Mutation checks

Each part of the fix was broken in turn and restored from a backup, and each break failed the expected tests:

  • Old no-input UpdateBranch: the journal test fails.

  • No wait on an accepted update: the 202 test fails.

  • No up-front fork check: the fork test fails.

  • No 072 skip: the 072 test fails.

  • No stop check: five tests fail.

  • No note dedupe: three tests fail.

  • pkg/holdguard, one break at a time: pending ignores the base; settle on the same head; a stop keeps the request; a stop ignores the base; the note ignores the reason. Each fails one of the new package tests.

  • No observed-held-head step (store.Touch in the snapshot step): the settled-update lift test fails.

Review question: is a settled update drift at lift time?

The hive's review asked whether the merge commit from an update that lands between ticks, which SettleUpdateRequest leaves out of the baseline's commit set, reads as new commits when a person lifts the hold. It does not, as long as the hive saw it while the PR was held. Each tick, the snapshot step records the held head (store.Touch, ObservedHeldHeadSHA) before the mergeability step settles the request. At lift, LiftCoversObservedHead accepts that head (#13603), and the commit set is only used to describe drift that has already been found. If the lift lands before the hive sees the new head, the PR is re-held with a drift note. This PR does not change that case, and it errs toward a fresh review. TestEnforceHoldGuardSettledUpdateIsNotDriftAtLift covers both orders.

Not verified

  • No live hive. Verified against the fake GitHub only.
  • base.sha freshness. The retry-on-main-moved rule relies on base.sha in the pulls list changing when main moves. PR follow-up already relies on the same field for the same purpose. I have not confirmed how quickly GitHub refreshes it in the list response.
  • Fork refusal text. The 422 and 403 fork-refusal messages in the tests follow the automerge classifier's patterns. I have not captured them live.
  • Merge-request watcher. pkg/github/merge_request_watcher.go still calls the no-input UpdateBranch for merge requests with update_branch, so a second request on the same PR would replay the first update the same way. Left out of this PR, because pinning it to the requested head changes that path's behaviour.

Contributor checklist

  • Changelog: changelog.d/fixed-hold-guard-branch-update-retries.md.
  • Targets v6.
  • The title uses the emoji convention.
  • Commits include DCO sign-off.
  • No docs describe this path.
  • No secrets, credentials or local runtime state are committed.

πŸ€– Generated with Claude Code

https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1

The hold guard's keep-mergeable update called UpdateBranch, whose
mutation-journal identity is the PR alone. After the first update, every
later one replayed as "already applied" without reaching GitHub, so the
branch stayed behind and the guard warned every pass.

- Call UpdateBranchExpectedHead with the head and base, so each pair is
  its own journaled attempt; held PRs now carry BaseSHA.
- An accepted (202) update whose head is not visible yet is waited on;
  one unmoved after 30 minutes stops as update-not-landed. A request
  settles when the head moves, without advancing the baseline.
- Refusals are classified with the automerge classifier, now exported
  from pkg/github: workflows permission, fork not editable (also up front
  when maintainer edits are off), conflict and up-to-date stop for that
  head and base until either moves. Transient failures still retry.
- One note per head and reason tells a person to update the branch; a
  conflict keeps its existing notice.
- A PR returned to its contributor (072) is not updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1
Signed-off-by: Danathar <Danathar@users.noreply.github.com>
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Oct 26, 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 danathar 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 26, 2026
@hivecommons-hive hivecommons-hive Bot added author/clanker A contributor's own hive authored this PR under their login 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 labels Oct 26, 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.

The diagnosis holds and the fix looks correct to me; one open question for the author, no defects found.

Verified on this head (d059167):

  • Root cause is real. UpdateBranch calls updateBranch(ctx, repo, number, "", "") (src/pkg/github/pullrequest.go:559-561), which leaves inputs == nil (pullrequest.go:580-583), and the mutation journal's logical ID is derived from the claim's Inputs (src/pkg/convergence/mutation/boundary.go:299-316, journal.go:175). So the identity really was repo + branch_update + PR number, and every later update for that PR replayed as applied. UpdateBranchExpectedHead adds expected_head_sha/observed_base_sha to the inputs, which gives one journal entry per (head, base) as the body describes.
  • UpdateBranchExpectedHead and PullRequest.BaseSHA already exist on v6 (pullrequest.go:568, client.go:986); this PR only starts populating BaseSHA in fetchPRsWithDetails. go build ./cmd/hive/ ./pkg/github/... ./pkg/holdguard/ passes on this head.
  • SettleUpdateRequest runs before the behind|dirty filter (main_helpers.go:305-308), so a 202 that lands between ticks is cleared even when the PR is no longer behind; UpdatePending cannot then re-match because the head differs.
  • ClassifyBranchUpdateError is a verbatim move out of automerge_sweep.go (type alias + const aliases keep the sweep's call sites unchanged), so the sweep's behaviour is not altered.
  • WorkflowRefusedHeadSHA written by older builds is still honoured by UpdateStoppedFor (holdguard.go:376-378), so no retry storm on upgrade.

Open question (not a defect): when an accepted update lands between ticks, SettleUpdateRequest clears the request but deliberately does not AdvanceHygiene (holdguard.go:441-447), so the hive's own merge commit is never added to the baseline CommitSHAs. The lift check is covered by ObservedHeldHeadSHA as the comment says β€” but does the snapshot/drift step treat that App-authored merge commit as new commits on a held PR and post the drift notice? If drift is already keyed off ObservedHeldHeadSHA rather than CommitSHAs, ignore this; if not, the 202 race turns into a spurious drift comment instead of a spurious WARN. I did not trace the snapshot step far enough to settle it.

β€” hive: agent=reviewer-2 backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive hivecommons-hive Bot added stage/072-returned-to-contributor Contributor PR: CI red, changes requested or conflicting; diagnosis posted, author must act and removed stage/050-review-pending Review swarm requested; awaiting a verdict for the current head labels Oct 26, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor

@Danathar this PR is back in your court (stage/072-returned-to-contributor). The hive will not merge it until the head changes.

Failing checks:

  • check-failure

Reply here or push a new head to this branch (same sign-off); CI and the review swarm re-run automatically and the PR returns to the normal path.

Your relay session received this as a fix-pr task; your current task is parked (claim kept alive) until this PR is green.

@hivecommons-hive

Copy link
Copy Markdown
Contributor

The only red required check, relative links and anchors resolve, is PR-local: this PR adds lines to src/pkg/github/client.go above RankActionableIssues, so the line-cited anchor in the docs drifted:

DRIFT src/docs/scale-envelope.md:37 src/pkg/github/client.go:4707-4717 -> lacks anchor(s) near cited range: AgeMinutes

On head d059167 the function now spans client.go:4712-4722 (sort by AgeMinutes at 4720). Fix in one line of src/docs/scale-envelope.md:37:

-     (`RankActionableIssues`, `src/pkg/github/client.go:4707-4717`, descending `AgeMinutes`).
+     (`RankActionableIssues`, `src/pkg/github/client.go:4712-4722`, descending `AgeMinutes`).

(The branch lives in your fork, so the hive cannot push this for you.)


🐝 Hive Agent: scanner | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown

β€” hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Open PR overlap check

Base branch: v6

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

PR Title Relation Files
#14399 🌱 chore: forward-merge v5 β†’ v6 (slice 1 of 2: through 25dd844, before the pkg/agentβ†’crux/agent rename) CONFLICT src/docs/scale-envelope.md

@hivecommons-hive

Copy link
Copy Markdown
Contributor

hive/ci-report (head d059167): the coverage check is PR-local β€” the gate reports holdguard: 75.3% (floor 90%) (job 121788688554); coverage is not red on v5.

Cause: the PR adds ~10 exported/unexported funcs to src/pkg/holdguard/holdguard.go (UpdateStoppedFor, UpdateStopNoted, MarkUpdateRequested, UpdatePending, SettleUpdateRequest, MarkUpdateStopped, MarkUpdateStopNoted, UpdateStoppedCommentBody, clearUpdateRequested) but all new tests live in src/cmd/hive/hold_guard_*_test.go. The gate scores per package without -coverpkg, so coverage exercised from cmd/hive does not count toward pkg/holdguard.

Fix: add table tests in src/pkg/holdguard/holdguard_test.go covering those functions directly β€” including the no-op paths (empty head, untracked PR), the legacy WorkflowRefusedHeadSHA branch of UpdateStoppedFor, UpdatePending before/after UpdateConfirmWindow, SettleUpdateRequest with a moved vs unmoved head, and each UpdateStop* reason through UpdateStoppedCommentBody. That should clear the 90% floor without touching the cmd/hive tests.


🐝 Hive Agent: scanner | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown

β€” hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor

hive/check-report: relative links and anchors resolve fails on this PR's own diff (job 121784248884):

DRIFT src/docs/scale-envelope.md:37 src/pkg/github/client.go:4707-4717 -> src/pkg/github/client.go lacks anchor(s) near cited range: AgeMinutes

The one-line insertion in src/pkg/github/client.go shifts RankActionableIssues; on this branch the function spans 4712–4720 (func RankActionableIssues at 4712, the AgeMinutes comparator at 4720), so the cited range no longer covers the anchor. Fix: in src/docs/scale-envelope.md:37 change src/pkg/github/client.go:4707-4717 β†’ src/pkg/github/client.go:4712-4720 (re-run bash src/scripts/check-doc-citations.sh or whichever citation checker the relative links and anchors resolve job calls to confirm). The coverage job on the same run fails fast at the shard-artifact stage and should be re-read after the next push.


🐝 Hive Agent: scanner | Instance: hosted-available-oke-11-placeholder-r05x | SHA: b05e7ba6f

β€” hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88

Danathar and others added 2 commits October 27, 2026 05:16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1
Signed-off-by: Danathar <Danathar@users.noreply.github.com>
…ifted citation

The coverage gate scores pkg/holdguard on its own tests, and the new
update-request and update-stop methods were only exercised from cmd/hive
(75.3% against the 90% floor). Test them in the package: a request is
awaited for its own head and base, expires after UpdateConfirmWindow and
settles once the head moves without moving the baseline; a stop holds for
one head and base and survives a reload; the note is per head and reason;
the comment names each reason.

Answer the review's open question with an end-to-end test: an update that
lands between ticks is not drift at lift time when the hive saw the merge
while the PR was held, because the snapshot step records that head before
the update settles (hivecommons#13603). A lift before the hive sees it still re-holds.

The BaseSHA line in client.go moved RankActionableIssues down one line, so
the scale-envelope citation now names 4712-4722.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1
Signed-off-by: Danathar <Danathar@users.noreply.github.com>
@kubestellar-prow kubestellar-prow Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Oct 27, 2026
@Danathar

Copy link
Copy Markdown
Contributor Author

Pushed a13a0c6, on top of a merge of current v6:

  • Docs citation: src/docs/scale-envelope.md:37 now cites client.go:4712-4722. check-docs-citations.py passes.
  • Coverage: new pkg/holdguard/update_state_test.go tests the update request and stop methods inside the package. Coverage is 99.6%, against the 90% floor.
  • Review question (settled update vs. drift at lift): no false drift note when the hive saw the merge while the PR was held. Each tick the snapshot step records the held head (store.Touch) before the request settles, and LiftCoversObservedHead accepts that head at lift (πŸ› [v6] hold guard re-holds a PR that a maintainer reviewed at its current head, because a rebase happened earlier during the holdΒ #13603). A lift before the hive sees the merge still re-holds, as it did before this PR. TestEnforceHoldGuardSettledUpdateIsNotDriftAtLift covers both orders. Details are in the PR description.

@github-actions github-actions Bot added the conflicts-with-open-pr Open PR has a merge conflict with another open PR label Oct 27, 2026
@hivecommons-hive hivecommons-hive Bot added stage/050-review-pending Review swarm requested; awaiting a verdict for the current head and removed stage/072-returned-to-contributor Contributor PR: CI red, changes requested or conflicting; diagnosis posted, 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.

Reviewed β€” no findings from this perspective.

β€” hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Hive filed review findings that do not block this PR as backlog items:

Cap: 10 backlog item(s) per PR per day.

@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=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.

Correctness review (read at a13a0c6, plus pkg/github/pullrequest.go, client.go enrichment, and the lift path in cmd/hive/main_helpers.go). No blocker; two small in-scope notes.

  1. Stale expected_head_sha 422 is recorded as a "conflict". The guard now pins the update to the enumerated head (src/cmd/hive/main_helpers.go:328, via UpdateBranchExpectedHead β†’ PullRequestBranchUpdateOptions{ExpectedHeadSHA} at src/pkg/github/pullrequest.go:594-596). When the author pushes between enumeration and the call, GitHub answers 422 "expected head sha didn't match current head ref"; ClassifyBranchUpdateError has no case for that text and its 422 fallthrough returns BranchUpdateConflict (src/pkg/github/branch_update_refusal.go:55). The guard then MarkUpdateStopped(..., "conflict"), logs a WARN "stopped keeping held PR mergeable" with reason=conflict (main_helpers.go:374-380), and on a dirty PR posts ConflictCommentBody (main_helpers.go:398-407). It self-heals next tick because the head moved, but the stop/log/comment are for a race, not a conflict. The old call had no expected head, so this path is new. Suggested fix in the shared classifier (the sweep also passes a head, so it benefits too):
	case http.StatusUnprocessableEntity:
		switch {
		case strings.Contains(msg, "expected head sha"):
			// The head moved under us; the next pass sees the new head.
			return BranchUpdateOther
		case strings.Contains(msg, "maintainer") || strings.Contains(msg, "permission") || strings.Contains(msg, "not authorized"):

(src/pkg/github/branch_update_refusal.go:47-48; BranchUpdateOther lands in the guard's transient branch at main_helpers.go:339-345, which only logs.)

  1. Legacy records now get a comment. UpdateStoppedFor maps an old WorkflowRefusedHeadSHA to UpdateStopWorkflowPermission (src/pkg/holdguard/holdguard.go:383-385), and the stopped path calls noteHeldPRUpdateStopped every pass (main_helpers.go:314-316), so every pre-upgrade refused PR still behind will receive one UpdateStoppedCommentBody on the first pass after deploy. Reasonable, but the PR body's "Legacy records" section says only that they still stop β€” worth a line there so nobody reads the burst of notes as a bug.

Checked and fine: actionable.PRs.Held is enriched through EnrichCIStatus β†’ enrichPRCI (client.go:2477-2492), so the up-front FromFork && !MaintainerCanModify stop reads the full-PR value, not the list endpoint's absent field. The settled-update/no-baseline-advance behaviour matches what the old code already did in the 202 race (warn, never advance), so lifts by a bot/hive actor behave as before.

β€” 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.

Reviewed (style/maintainability) β€” no blockers; one advisory.

ClassifyBranchUpdateError now lives in src/pkg/github/branch_update_refusal.go:32-58, but its only table test is still TestClassifyUpdateBranchError in src/pkg/github/automerge/automerge_update_branch_error_test.go:21, which exercises the one-line forwarder classifyUpdateBranchError (automerge_sweep.go:2473-2475) and the const aliases. Tests normally sit beside the code they cover, and the pkg/github floor in .github/workflows/v2-tests.yml:1632 is scored without -coverpkg, so the moved body counts as uncovered there. Suggest moving (or copying) the table into src/pkg/github/branch_update_refusal_test.go against BranchUpdate*, and then either dropping the automerge aliases (single call site at automerge_sweep.go:2438) or leaving them β€” your call; the alias layer is fine as-is if you prefer the smaller diff.

β€” hive: agent=reviewer-2 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 merged commit e0333c1 into hivecommons:v6 Oct 27, 2026
59 of 62 checks passed
@hivecommons-hive

Copy link
Copy Markdown
Contributor
Evidence bundle `#14401a13a0c6` sha256:ddfa0bfb7e9e4a6745f06c356736d250561986447fcd8384724cbceb2ccb663c β€” download from the dashboard or `hivectl review evidence #14401`.

@hivecommons-hive

Copy link
Copy Markdown
Contributor

merged by Hive trusted-author auto-merge: author @Danathar holds role owner and GitHub permission maintain on repo; CI green at a13a0c6.

@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
hivecommons-hive Bot pushed a commit that referenced this pull request Oct 27, 2026
…om-base (#14535)

* πŸ› fix(holdguard): advance the held baseline only on a proven merge-from-base

After an accepted update-branch, maintainHeldPRMergeability refetched the
PR's commits and advanced the hold baseline to whatever head it found. A
contributor push landing between the 202 and the refetch was therefore
adopted as hive hygiene, moving the reviewed baseline past unreviewed
commits.

The advance now requires the new head to be a two-parent commit whose
parents are the head the hive asked to update and the base SHA
(holdguard.IsHygieneMerge). Any other head leaves the baseline pinned, so
the lift check reads the move as drift and the PR goes back to a human.
ListPRCommits now carries the parent SHAs to make that proof possible.

Hive-Run: #14534
Hive-Plan: review-backlog security finding (PR #14401 out-of-scope)
Hive-Spec: holdguard#hygiene-baseline-advance

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HRH6kjKSaVf8mEsXFx1uAd
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* πŸ› fix(holdguard): prove the hygiene merge's base parent against the live base

Review of #14535: the second-parent check compared against pr.BaseSHA,
which is empty for every held PR on the polling path (fetchPRsWithDetails
never set it) and, when set, is the enumeration-time tip rather than the
tip update-branch actually merges. Either way the hive's own merge failed
the proof and read as drift at lift, regressing #10437.

- Populate BaseSHA for held PRs and refresh it from the enrichPRCI detail.
- IsHygieneMerge keeps the strict first-parent proof but takes an onBase
  membership predicate for the second parent: BaseSHA is the fast path,
  otherwise Client.CommitOnBranch asks GitHub's compare whether the base
  branch contains the merged parent. Errors fail closed.
- Document that BaseSHA now feeds the hold guard (fail-closed).
- Test the base-moved and BaseSHA-unknown cases.

Closes #14534

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

* πŸ“ docs(scale-envelope): re-anchor RankActionableIssues citation after client.go shift

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

---------

Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Karibhou <Karibhou@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author/clanker A contributor's own hive authored this PR under their login conflicts-with-open-pr Open PR has a merge conflict with another open PR dco All commits are DCO signed-off dco-signoff: yes Indicates the PR's author has signed the DCO. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant