Skip to content

✨ feat(mergelane): serialized lane core with durable front record and pinned merge - #10897

Merged
hivecommons-hive[bot] merged 2 commits into
v6from
scanner/fix-10888
Oct 6, 2026
Merged

hivecommons-hive[bot] merged 2 commits into
v6from
scanner/fix-10888

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Closes #10888
Part of #10884

Summary

New self-contained package src/pkg/mergelane/ (4 files incl. tests): the serialized lane core for hive-serialized repositories. It is not wired into any merge path (next child, #10889), so nothing changes for direct repositories or any existing test.

Durable front record (store.go)

  • One JSON file under /data/mergelane/fronts.json, reloaded under an exclusive flock and rewritten atomically (unique temp name, fsync, rename) on every transaction, the same pattern as the convergence claim ledger. A corrupt file is refused and left untouched, so the lane fails closed.
  • Per (repo, target branch): front (PR, epoch, stage, deadline, evaluated/pinned head), waiters ordered oldest-eligible first, last exit, and a monotonic epoch that fences stale holders. The record blocks no matter which mode the convergence ledger runs in.

Gate API (lane.go)

  • Acquire(repo, branch, pr, path): records eligibility. The oldest eligible PR reaches the front. Everyone else gets deferred: not at the front of the lane and their position.
  • Advance(ctx, repo, branch, pr, authorize): runs one round for the front PR (validate → update → wait → final re-check → pinned merge → verify). Non-front callers are deferred and no GitHub call is made for them.
  • Release(repo, branch, pr, reason): called when the path no longer finds the PR eligible. If the PR becomes eligible again, it rejoins at the back.
  • Snapshot: returns the durable state for later dashboard/API use.
  • New fences every front left by a previous process (epoch bump, validation reset), so after a restart the validation is repeated in full.
  • GitHub is reached only through a small GitHub interface (PR, branch tip, compare tip...head, the lane-only BranchRulesResult from ✨ github: lane-only branch-rules reader (ruleset required checks, up-to-date enforcement, native merge-queue detection) #10887, check runs, pinned UpdateBranch(expectedHead), head-pinned Merge, commit parents, PRs for a commit). The policy hooks (strategy via config.MergeStrategyHiveSerialized from ✨ config: per-repo merge_strategy (direct | hive-serialized) with validation, asymmetric permission and API #10886, auto-merge, pause, labels, front timeout override) are callbacks. Any callback left nil fails closed.

State machine (R8–R13, R15–R19, fail closed)

  • Staleness is detected via the compare API, never mergeable_state: behind. Only the front PR is updated, with a merge update pinned to the evaluated head. A changed head means no update; the new head is re-evaluated and the PR keeps the front. After an update the lane waits for the head to move off the pinned SHA.
  • Required checks: every required check must pass on the exact head (skipped/neutral pass, as GitHub treats them). Missing, queued or running means wait. Failed, cancelled, timed_out or action_required leaves the front. An unknown or empty required set means no merge, with a reason.
  • Final live re-read right before the merge: head, tip (equal to the validated tip), open, draft, base, mergeability (unknown means no merge this round), the required set and its results, hold/do-not-merge/exempt/pause labels, repo pause, strategy, auto-merge, and the path's authorization callback.
  • Merge: REST merge with the head SHA pinned. A 409 (ErrHeadMoved) restarts validation. Any other error means no merge this round. The epoch is fenced right before the merge call.
  • Post-merge verify: the merge commit's first parent must be the validated tip. If it isn't, Hive raises an alert naming the merged PR and the unexpected commit (and its PR).
  • The front timeout defaults to 60 minutes (hive-wide), can be overridden per repo, and restarts on every update. Switching to direct ends validation without a merge and without a GitHub call.
  • Audit (R31): every front entry and exit, deferral, branch update, merge, refusal, restart reset and alert is recorded with PR, head, tip and reason. A repeated identical wait reason is audited once.

Note: UpdateBranchExpectedHead (#10864) is not on v6 yet. The lane's interface takes the expected head, and the wiring child will adapt it to that helper.

Acceptance criteria covered (unit/scenario tests with fakes, injected clock, no sleeps)

  • AC6: two green PRs merge one at a time; the second is updated and re-checked on the new tip.
  • AC8: oldest-eligible order; a PR that leaves the front rejoins at the back.
  • AC9 / AC10: a stale PR reported "clean" is detected and updated with a pinned update; no update when its head changed; the lane waits for the update to land.
  • AC11: a moved branch stops the merge, the PR is updated again, and the reason is audited.
  • AC12: a head change during validation, or a 409 at the merge, means no merge.
  • AC13: hold, on-hold, do-not-merge, exempt and pause labels, or a repo pause, stop the merge, whether added between rounds or at the final re-check.
  • AC14: a missing required check never passes; the PR leaves only on timeout.
  • AC17: closed, draft, retargeted, switched to direct, auto-merge off, path authorization lost, a new required check, a check failing during validation.
  • AC18: check results classified as GitHub does (skipped and neutral pass; failure, cancelled, timed_out and action_required fail).
  • AC19: mergeability unknown or any failed API call, including in the final re-check, means no merge for that round and the reason is audited.
  • AC20: a failing check moves the lane on, and PRs further back are not updated.
  • AC21: hive-wide timeout, per-repo override, reset on update.
  • AC22: restart keeps one front per branch, repeats the validation in full and fences the pre-restart holder.
  • AC23: a merge on an unexpected tip raises an alert naming both PRs.
  • AC25: every update is a merge update pinned to the head; nothing rebases or force-pushes.
  • AC35: every entry, exit, update, merge and refusal is audited with PR, head, tip and reason.

— hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88 requested_by=@Danathar

…inned merge

Signed-off-by: scanner <scanner@hive.kubestellar.io>
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Oct 6, 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/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Oct 6, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

hive/awaiting-ci: CI pending — sweep will merge when green.


🐝 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 6, 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.

No conflicting open PRs or same-file overlaps were found.

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Read lane.go and store.go in full against the v6 tree; the core looks correct to me. The two red checks are pre-existing on v6 HEAD, not from this PR.

What I verified:

  • Every pkg/github / pkg/config symbol the package borrows exists on v6 with the semantics the lane assumes: BranchRulesResult.Known/Required/Reason/SortedRequired (src/pkg/github/branch_rules.go:30-44,150), ClassifyRequiredCheck mapping skipped/neutral→passed and failure/cancelled/timed_out/action_required→failed (branch_rules.go:136-147), MergeableFromState (client.go:3337), HasHoldLabel/HasExemptLabel (client.go:2342,2505), config.MergeStrategyHiveSerialized (src/pkg/config/repo_policy.go:29).
  • Fencing: round.save/round.leave both re-check PR+Epoch under the store transaction before writing, and merge() saves StageMerging (fenced) before the GitHub Merge call, so a front taken away by expiry/Release/restart cannot complete a merge round. New bumps the epoch and clears EvaluatedHead for every persisted front, so a pre-restart holder is fenced as the body claims.
  • Store: update takes the in-process mutex and flock(LOCK_EX) on fronts.json.lock, reloads, applies, then temp+fsync+rename and a directory fsync — matches the described ledger idiom; an unparseable file errors out of OpenStore rather than being overwritten.
  • Post-merge verify checks parents[0] == validated tip. That is correct for both merge methods hive's MergePR issues (squash, and merge for forward-merges — src/pkg/github/pullrequest.go:245-256); there is no rebase method anywhere in pkg/config/pkg/github, so the first-parent check cannot false-alert on a multi-commit rebase. Worth keeping in mind if rebase is ever added.
  • No existing file is touched, so "nothing changes for direct repositories" is accurate.

CI on 4ce2f22: test (rest 5/5) failed in setup (npm missing from PATH, the #10518 runner cache issue — no Go test failed in the log), and relative links and anchors resolve reports 102 api-reference citation drifts in pkg/dashboard/api.go, which this PR does not modify. Both checks are also failure on current v6 HEAD (gh api repos/hivecommons/hive/commits/v6/check-runs), so a re-run / base fix is what's needed, not a change here.

Nothing blocking from my side; a maintainer should sanity-check the state-machine semantics against #10884's R8–R13 since that is a design judgement, not something I can settle from the code alone.

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

@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Deferred to shared CI incident #10899: test (rest 5/5) and relative links and anchors resolve fail identically on the v6 default branch (head 009369a), so this PR's red checks are inherited, not PR-local. Baseline fix is #10900; this PR will be re-run once v6 is green.


🐝 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

…i-reference citations

TestInvalidMergeStrategyFailsValidationAndStopsAutoMerge built an agentless
config, so Validate() stopped at 'at least one agent must be configured'
before reaching the merge_strategy check. Use ValidateWithOptions with
RequireAgents unset, matching the other agentless config tests.

Also rewrite the 102 api-reference.md source citations drifted by the
pkg/dashboard/api.go route additions (check-api-reference-citations --fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <scanner@hive.kubestellar.io>
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

hive/awaiting-ci: pushed e01ca19 — agentless config test now uses ValidateWithOptions (RequireAgents unset) and api-reference.md citations re-synced via check-api-reference-citations.sh --fix. CI pending — sweep will merge when green.


🐝 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
hivecommons-hive Bot merged commit 130603a into v6 Oct 6, 2026
62 of 63 checks passed
@kubestellar-prow
kubestellar-prow Bot deleted the scanner/fix-10888 branch October 6, 2026 18:37
@github-actions

github-actions Bot commented Oct 6, 2026

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

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

Labels

copilot 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.

0 participants