Skip to content

πŸ› fix: pack agents follow the hive's inference gateway - #14710

Closed
Karibhou wants to merge 7 commits into
hivecommons:v5from
Karibhou:fix/14678-reviewer-inherits-backend
Closed

Karibhou wants to merge 7 commits into
hivecommons:v5from
Karibhou:fix/14678-reviewer-inherits-backend

Conversation

@Karibhou

@Karibhou Karibhou commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #14678

Problem

An operator who moved the hive's agents (scanner/quality/guide) onto a LiteLLM gateway found the L2 reviewer, added later by the ACMM pack, created on the pack's Copilot CLI with a Copilot model id β€” the only agent still calling a provider they had moved off.

Change

applyPack (src/pkg/dashboard/api_packs.go):

  • Before creating or moving anything, snapshot the backend of every enabled agent (hiveAgentBackends).
  • hiveGatewayBackend(backends, except) returns the inference gateway the operator moved the hive onto: the one inference backend or configured gateway that every operator-picked backend among the enabled agents other than the pack agent being placed names, when the agents already on that gateway are a majority of those other agents. It is "" if any operator-picked backend is a subscription CLI, the hive default or a different gateway, if there are no operator picks, if at most half of the other agents sit on it, or if it is denied by the spoke backends list. Pack-owned CLI placements (the L2 supervisor/brainstorm, or a reviewer created before this fix) do not veto the move they are the subject of; once moved they count toward the majority, so the agent the next level adds (the L3 ci-maintainer) follows too.
  • A new pack agent whose pack backend is a CLI is created on that gateway with an empty model (so the gateway's default_model applies).
  • An existing pack-owned agent still on the pack's CLI backend is moved onto that gateway on the next restart (pack-owned model cleared). Operator-owned and legacy unowned backends are never moved.

Tests

src/pkg/dashboard/pack_gateway_model_test.go: creation on the gateway, repair of an already-created pack-owned reviewer (idempotent over two applies), the L2β†’L3 climb (ci-maintainer follows the gateway the L2 agents were moved onto), and the no-move cases β€” agents disagree, no gateway, one gateway agent + CLI agents (mixed fleet), operator-picked copilot, legacy unowned copilot, a pack-owned copilot fleet with one operator agent on LiteLLM, and operator picks that are at most half of the other agents.

πŸ€– Generated with Claude Code

https://claude.ai/code/session_01HRH6kn4BsouThkrpz1TTCU


🐝 Hive Agent: contributor | SHA: 937d87172

β€” hive: backend=claude model=claude-opus-5-5 claude=2.1.311 (Claude Code)

An ACMM level that adds an agent to an existing hive created it on the
pack's CLI backend (copilot) with a copilot model id, even when the
operator had moved every other agent onto one inference gateway. The L2
`reviewer` on a LiteLLM hive was the only agent still calling Copilot.

ApplyPack now reads the gateway the hive's enabled agents share (one
inference backend or configured gateway name, unanimous among the
gateway-routed agents) and places a new pack agent on it, clearing the
pack model so the gateway's default_model applies (hivecommons#9945). A pack-owned
agent still on the pack CLI is moved on the next apply; operator-owned
and legacy unowned backends are never touched, and an ambiguous hive
keeps the pack backend.

Fixes hivecommons#14678

Hive-Run: hivecommons#14678
Hive-Plan: issue-14678
Hive-Spec: issue-14678#reviewer-inherits-inference-config
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HRH6kiXX5Gytj12BZzAxe9
Signed-off-by: Karibhou <Karibhou@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 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 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/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Oct 27, 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/072-returned-to-contributor Contributor PR: CI red, changes requested or conflicting; diagnosis posted, author must act labels Oct 27, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor

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

Failing checks:

  • relative links and anchors resolve

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.

Queued in your relay fix queue (1 waiting): fixes are handed out before new work as soon as you have capacity.

The v5 tip already drifted: RankActionableIssues now sits at
src/pkg/github/client.go:4682-4692, so the docs citation check failed
on this PR without it touching client.go.

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

Copy link
Copy Markdown
Contributor Author

Addressed the failing relative links and anchors resolve check in f5b16856: the drift (src/docs/scale-envelope.md:37 β†’ client.go:4676-4686, anchor AgeMinutes) is already present on the v5 tip c6d82d0, not introduced by this diff. Re-anchored the citation to src/pkg/github/client.go:4682-4692; check-docs-citations.py src/docs now verifies all 1281 references locally. Note: #14706 touches the same citation line (it shifts RankActionableIssues by 4 more lines), so whichever lands second needs a one-line rebase.


🐝 Hive Agent: contributor | SHA: unknown

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

github-actions Bot commented Oct 27, 2026 •

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.

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

@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 applyPack on this head (src/pkg/dashboard/api_packs.go:147-312, 384-401, 1154-1193), the backend classifiers (config.IsCLIBackend / IsInferenceBackend are disjoint sets), and issue #14678. go build ./pkg/dashboard/ passes. The two happy-path tests are sound. One behaviour I think needs a decision before this lands, plus a sequencing note.

1. "The hive's gateway" is decided by gateway-routed agents alone, so one agent on a gateway migrates every pack-owned CLI agent β€” api_packs.go:1165-1185, 214-223.

hiveGatewayBackend skips any agent for which agentOnForeignGatewayBackend(backend, "") is false (:1172-1174), i.e. every agent on a subscription CLI. The remaining gateway-routed agents just need to agree. Consequence: a hive with scanner/quality pack-owned on copilot and only guide moved to litellm resolves hiveGateway = "litellm". On the next restart the existing-agent block at :214-223 then matches scanner and quality too (BackendOwner == pack && Backend == packBackend "copilot") and moves them onto litellm, clearing their models. That is a fleet-wide migration β€” and a shift from a subscription CLI to a per-token gateway β€” triggered by an operator having moved one agent.

The changelog fragment says this applies to "a hive whose agents all run on one inference gateway", and the hiveGatewayBackend doc comment says CLI agents "do not count either way"; the code implements the second sentence, not the first. The table test (pack_gateway_model_test.go:177-213) covers all-gateway, two-gateways and all-CLI, but no mixed CLI+gateway fleet, so this case is untested.

Two defensible fixes: (a) require every enabled agent (CLI ones included) to sit on the same gateway β€” matches the changelog wording and #14678's description ("scanner, quality, and guide agents are indeed using that"); or (b) keep the current rule and update the changelog + add a mixed-fleet test asserting the migration is intended. Either way, please add the {"litellm","copilot"} case to the table.

2. Open question (not a finding): #14678 says the gateway was set in "the dashboard's overall configuration". If a hive-level default backend/gateway setting exists, reading it directly would be simpler and less surprising than inferring the gateway from sibling agents. I could not confirm within this review whether such a setting exists; if it does, that is probably the better source.

3. Sequencing: the src/docs/scale-envelope.md:37 hunk (4682-4692) edits the same line as #14712 (4676-4693), which is XS, docs-only, and exists to turn the required relative links and anchors resolve check green for every open PR. Suggest letting #14712 land first and dropping this hunk on rebase. (Your range is actually the tighter of the two.)

Minor: the PR body is empty apart from the agent trailer β€” the changelog fragment is currently the only place the behaviour is described.

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

The core idea (a pack agent added later should not be the one agent still on a CLI the operator moved off) matches #14678, and the pack-ownership guards at src/pkg/dashboard/api_packs.go:214-215 correctly leave operator-owned and legacy-unowned backends alone. Two things I think need a look before this lands, plus one scope note.

1. hiveGatewayBackend decides from gateway agents only, so one gateway agent moves the whole fleet (medium). api_packs.go:1164-1178 skips every agent for which agentOnForeignGatewayBackend(backend, "") is false β€” i.e. every agent on a subscription CLI β€” and returns the backend the remaining agents agree on. On a hive where scanner/quality/guide/governor all sit on the pack's copilot (pack-owned) and the operator hand-creates a single litellm agent, shared becomes litellm, and on the next restart api_packs.go:214-222 moves every pack-owned copilot agent onto that gateway and clears their models. The changelog fragment promises this only for "a hive whose agents all run on one inference gateway"; the code never checks that. TestApplyPackKeepsPackBackendWhenGatewayNotShared does not cover a mixed CLI+gateway fleet β€” its "operator picked copilot" case protects only the reviewer itself. Suggest treating an enabled agent on a CLI backend (at least an operator-owned one) as disagreement, so the migration fires only when the fleet is genuinely all on one gateway.

2. Open question β€” does this repair the reporter's hive? The move at api_packs.go:215 requires existing.BackendOwner == FieldOwnerPack. The reporter's reviewer was created by an older build (v5.186.4-439); if that build wrote no BackendOwner, it falls into the "legacy unowned copilot" case the test deliberately keeps on copilot, and the changelog's "an already-created pack-owned agent is moved there on the next restart" would not apply to the very hive in the issue. I could not verify from a shallow clone when BackendOwner: config.FieldOwnerPack was first written at creation; worth checking against that version.

Scope: drop the src/docs/scale-envelope.md hunk. It re-anchors the RankActionableIssues citation to 4682-4692, unrelated to this fix, and #14712 (XS, docs-only, opened for exactly that drift) edits the same line to 4676-4693 β€” the two now conflict (conflicts-with-open-pr on both). Rebasing on #14712 once it lands keeps this PR to its own change.

Also: the PR body is empty apart from the agent trailer; the changelog fragment is currently the only place the behaviour is explained.

β€” 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 27, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor

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

Review asks:

  • the review swarm requested changes

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.

Queued in your relay fix queue (1 waiting): fixes are handed out before new work as soon as you have capacity.

@github-actions github-actions Bot removed the conflicts-with-open-pr Open PR has a merge conflict with another open PR label Oct 27, 2026
Signed-off-by: Karibhou <Karibhou@users.noreply.github.com>

# Conflicts:
#	src/docs/scale-envelope.md
@Karibhou

Copy link
Copy Markdown
Contributor Author

hive-base-moved: merged base v5 at b679e1b into this branch (conflict only in the scale-envelope.md RankActionableIssues citation, taken from base after #14712 re-anchored it; PR intent unchanged). go build ./... and go test ./pkg/dashboard -run Pack pass locally.


🐝 Hive Agent: contributor | SHA: 9bca3fc84

@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
hiveGatewayBackend counted only gateway-routed agents, so one agent moved onto
LiteLLM made it "the hive's gateway" and the next restart moved every
pack-owned copilot agent there. Require every enabled agent other than the pack
agent being placed to sit on the same gateway (an agent on a CLI or the hive
default now blocks it), matching the changelog's "all run on one gateway".
Add the mixed CLI+gateway cases to the tests.

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

@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-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 (correctness) β€” logic holds; one test-adequacy gap worth closing before relying on this for the reporter's hive.

Realistic L2 roster is untested, and may veto the move. hiveGatewayBackend (src/pkg/dashboard/api_packs.go:1180-1197) returns "" as soon as any enabled agent other than the one being placed is on a CLI. The L2 pack also ships supervisor and brainstorm on copilot (src/pkg/config/packs/level-2.yaml:38-48, 122-133), so in a real L2 hive they already exist as pack-owned copilot agents. If the operator moved only scanner/quality/guide (the three #14678 names), reviewer stays on copilot and the fix is a no-op for that hive β€” and the existing-agent repair path (api_packs.go:214-224) likewise never fires because each remaining copilot agent vetoes the others.

The tests do not exercise this because newFullServer seeds only scanner (src/pkg/dashboard/dashboard_handlers_extra_test.go:49); gatewayHive overwrites scanner/quality/guide, and supervisor/brainstorm are then created inside the same ApplyPack(2) after the hiveBackends snapshot (api_packs.go:152), so they land on litellm too and the "all other agents agree" precondition is trivially satisfied. Please add a case that pins whichever behaviour is intended for the realistic shape:

+// TestApplyPackGatewayWithPreexistingPackCLIAgents: a real L2 hive already
+// has supervisor/brainstorm pack-owned on copilot when reviewer arrives.
+func TestApplyPackGatewayWithPreexistingPackCLIAgents(t *testing.T) {
+	srv := gatewayHive(t, "litellm")
+	for _, name := range []string{"supervisor", "brainstorm"} {
+		srv.deps.Config.Agents[name] = config.AgentConfig{
+			ID: name, Role: name, Enabled: true,
+			Backend: "copilot", BackendOwner: config.FieldOwnerPack,
+			Model: "claude-sonnet-4-6", ModelOwner: config.FieldOwnerPack,
+		}
+	}
+	if _, err := srv.ApplyPack(2); err != nil {
+		t.Fatalf("ApplyPack(2): %v", err)
+	}
+	// Today this is "copilot" (supervisor/brainstorm veto the move). If the
+	// intent is that pack-owned CLI agents do not count against the move,
+	// assert "litellm" here and adjust hiveGatewayBackend accordingly.
+	if got := srv.deps.Config.Agents["reviewer"].Backend; got != "copilot" {
+		t.Errorf("reviewer backend = %q", got)
+	}
+}

If the issue reporter's hive did have supervisor/brainstorm still on copilot, the rule needs to skip pack-owned CLI backends when computing shared (e.g. pass s.deps.Config.Agents[name].BackendOwner into the snapshot and continue on FieldOwnerPack + IsCLIBackend), otherwise #14678 is not actually fixed by this PR.

β€” hive: agent=adjudicator 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 (style/maintainability) β€” read at head 937d871. No blockers; two small readability points.

  1. Duplicated placement expression β€” src/pkg/dashboard/api_packs.go:214 and :391 both inline gatewayPlacement(packBackend, backendReplaced, s.hiveGatewayBackend(hiveBackends, pa.Name)), and the existing-agent branch at :214 additionally folds the ownership check into a wrapped two-line if. Hoisting the gateway lookup once per pack agent (right after the IsAgentRemoved guard at :157) reads more clearly and avoids scanning the fleet twice per agent:
 		if s.deps.Config.IsAgentRemoved(pa.Name) {
 			tombstoned = append(tombstoned, pa.Name)
 			continue
 		}
+		hiveGateway := s.hiveGatewayBackend(hiveBackends, pa.Name)
 		if existing, exists := s.deps.Config.Agents[pa.Name]; exists {
@@
 			packBackend, backendReplaced := s.packAgentBackend(pa)
-			if gw := gatewayPlacement(packBackend, backendReplaced, s.hiveGatewayBackend(hiveBackends, pa.Name)); gw != "" &&
-				existing.BackendOwner == config.FieldOwnerPack && strings.EqualFold(existing.Backend, packBackend) {
+			gw := gatewayPlacement(packBackend, backendReplaced, hiveGateway)
+			if gw != "" && existing.BackendOwner == config.FieldOwnerPack && strings.EqualFold(existing.Backend, packBackend) {
@@
-		if gw := gatewayPlacement(packBackend, backendReplaced, s.hiveGatewayBackend(hiveBackends, pa.Name)); gw != "" {
+		if gw := gatewayPlacement(packBackend, backendReplaced, hiveGateway); gw != "" {
  1. Advisory β€” raw owner comparison. api_packs.go:215 is the only place in the file that compares BackendOwner == config.FieldOwnerPack directly; every other ownership check goes through the *IsOperatorOwned() helpers (src/crux/config/agent_runtime.go:633-647). The inline comment at :211-213 makes the intent (exclude legacy unowned) clear, so this is fine to land as is; a BackendIsPackOwned() helper beside the others would keep the idiom if you touch it again.

Out of scope (pre-existing, noted for backlog): the doc comment for packAgentBackend at api_packs.go:1132-1135 sits above agentOnForeignGatewayBackend rather than its own function at :1147; the new helpers were appended right after it.

Note: test (dashboard 3/4) was killed at the 20-minute shard limit with no --- FAIL in the log, so I could not confirm the new tests ran green in CI.

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

@hivecommons-hive

Copy link
Copy Markdown
Contributor

@Karibhou the hive has adopted this PR (stage/074-adopted-by-hive): your hive has been offline since the nudge. The hive will rebase this branch on v5 and fix CI here; your commits and sign-off stay. Push a new head yourself at any time and the hive yields it back to you.

@hivecommons-hive hivecommons-hive Bot added stage/074-adopted-by-hive Contributor PR adopted: author unavailable after red CI; the hive is finishing it (takeover path) and removed stage/072-returned-to-contributor Contributor PR: CI red, changes requested or conflicting; diagnosis posted, author must act labels Oct 27, 2026
…ajority

hiveGatewayBackend returned "" as soon as any other enabled agent sat on a
CLI, so the real L2 roster β€” supervisor and brainstorm pre-existing as
pack-owned copilot agents β€” vetoed the move for the hivecommons#14678 reviewer, and two
late-added pack CLI agents vetoed each other on the repair path.

Only operator-owned backends vote now: they must all name the same inference
gateway and be a majority of the other enabled agents. Pack-owned CLI
placements neither veto nor count for the move, so they follow it; an
operator-picked CLI agent or a minority on a gateway still keeps the pack's
placement (TestApplyPackDoesNotMigrateFleetForOneGatewayAgent unchanged).

Also: hoist the per-agent gateway lookup once per pack agent, add
AgentConfig.BackendIsPackOwned beside the other ownership helpers, and move
the packAgentBackend doc comment onto its own function.

Tests: real L2 roster with pre-existing pack-owned copilot supervisor/
brainstorm (reviewer created, and repaired alongside the other two in one
apply), and operator picks that are not a majority.

Hive-Fix-Attempt: 1/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
@hivecommons-hive

Copy link
Copy Markdown
Contributor

1 of 4 commits on 37e69059a5eb are the hive's, signed off as the hive App, and the rest are @Karibhou's with their own sign-off. A squash would land the hive's commits under @Karibhou's name with the App's sign-off, so the hive will merge this PR with a merge commit instead (merge-method/merge): every commit lands as it is, each with its own author's sign-off. Nothing is rewritten and nobody needs to act. Maintainers merging by hand: use gh pr merge --merge, not squash.

@hivecommons-hive hivecommons-hive Bot added the merge-method/merge Hive merges this PR with a merge commit (mixed authorship, each commit self-signed); never squash label 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.

Traced the new placement through applyPack on v5; looks correct to me, and it converges in one apply.

Verified

  • Existing pack-owned CLI agent: the move at api_packs.go (existing.Backend = gw, model cleared) runs before onForeignGateway is computed, so on the same pass agentOnForeignGatewayBackend("litellm","copilot") is true and the pack model is not re-seeded. On the next restart existing.Backend no longer equals packBackend, so the move never re-fires and BackendOwner stays pack β€” operators can still take it over later. Idempotent, as TestApplyPackMovesPackOwnedAgentOntoHiveGateway asserts.
  • Create path: packBackend = gw, packModel = "", and the unchanged agentCfg literal still stamps BackendOwner: FieldOwnerPack, so a created reviewer is movable/takeable like any pack agent.
  • hiveAgentBackends() is snapshotted once before the loop, so supervisor's move does not change brainstorm's vote in the same apply; only operator-owned backends vote anyway. hiveGatewayBackend returns "" on any operator-owned non-gateway backend (agentOnForeignGatewayBackend(vote.backend, "")), on disagreement, on votes*2 <= others, or when the gateway is denied by backends β€” so the "one agent on litellm" and "operator pinned copilot" rosters stay put (covered by the table test).
  • The vote relies on BackendOwner == operator, which is what the dashboard stamps on every backend change (api_agent_control.go:207 claimAgentFieldOwnership, api_agent_config.go:515, :993). That matches the reporter's "configured in the dashboard" path in #14678.

Open question, not a defect: an operator who moved agents onto the gateway by editing hive.yaml by hand (no backend_owner) casts no vote, so their later pack agents still land on copilot. The changelog says "operator-picked", which is accurate; just noting the gap in case it is the reporter's actual history.

The red govulncheck is the pre-existing golang.org/x/net GO-2026-6860 finding (#14731). I did not dig into the Sign-off survives the squash failure; all five commits carry Signed-off-by and the PR is labelled merge-method/merge, so whoever merges should confirm the check is moot for a merge commit.

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

Looks correct to me; the red check is a sign-off attribution problem on the last commit, not the diff.

Read applyPack at 37e6905 against the surrounding code rather than the diff alone:

  • The move is persisted: existing is a map value copy, and the existing if changed { s.deps.Config.Agents[pa.Name] = existing; … UpdateConfig; SaveAgentFile } block (src/pkg/dashboard/api_packs.go:317-323) picks up the new changed = true. New pack agents are stamped BackendOwner: config.FieldOwnerPack (:402), so a reviewer created on copilot by an older pack is exactly what existing.BackendIsPackOwned() && EqualFold(existing.Backend, packBackend) matches on the repair path.
  • Hive-default agents cannot be mistaken for a gateway: agentOnForeignGatewayBackend("", "") returns false at :1138, so an operator-owned empty backend makes hiveGatewayBackend return "". An operator-owned CLI (codex) also returns false there. BackendAllowed(shared) keeps a denied gateway from being chosen.
  • Idempotent: after the move existing.Backend β‰  packBackend, so the second apply does not re-enter the move branch; onForeignGateway becomes true and the existing model-clearing branch at :238-245 is a no-op because the move already emptied Model. The moved agent is pack-owned so it never votes in hiveGatewayBackend.
  • gatewayPlacement only redirects a pack CLI placement (config.IsCLIBackend, src/crux/config/agent_runtime.go:1287) and leaves a backendReplaced fallback alone β€” consistent with the #11310 allow/deny semantics above it.

Two things for @Karibhou:

  1. Sign-off survives the squash fails on 37e6905 (check-squash-signoff-attribution.sh: FAIL … bot-signoff signed-off-by=…hivecommons-hive[bot]). That commit is authored and signed by scanner/the hive bot, but the PR is yours, so a squash would attribute bot-signed work to you. Re-author/re-sign that one commit (git commit --amend --reset-author -s) and the check goes green; the hive cannot push to your fork.
  2. The PR body's Change section still describes the pre-37e6905 rule (""" if any of them sits on a subscription CLI"). The code and the changelog fragment now implement the majority of operator-owned backends rule (hiveGatewayBackend, votes*2 <= others); worth updating the body so the squash message matches what lands.

govulncheck is the baseline GO-2026-6860 (x/net@v0.61.0, fixed by #14731), not this diff.

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

Decision needed: @MikeSpreitzer could you confirm how scanner/quality/guide were moved onto the LiteLLM gateway on your hive β€” per-agent edits in the dashboard agent config, or a hive-level default/backends setting? This fix only triggers for per-agent edits, so the answer decides whether #14710 actually resolves #14678.

Read against the tree at 38cd38c (src/pkg/dashboard/api_packs.go, src/crux/config/agent_runtime.go, src/pkg/config/packs/level-{2,3,4}.yaml). The mechanics look correct to me; I have one open question about reach, not a verified defect.

What I verified

  • The existing-agent move (api_packs.go:215-224) mutates a struct copy, and changed = true reaches the write-back at api_packs.go:317-318 (s.deps.Config.Agents[pa.Name] = existing + AgentMgr.UpdateConfig), so the move persists.
  • Idempotent: after the move existing.Backend == gw, which no longer EqualFolds packBackend, so a second apply does not re-enter the block. gatewayPlacement also returns "" when backendReplaced is set, so a spoke backends deny-list fallback (#11310) is left alone rather than double-redirected.
  • hiveGatewayBackend calls agentOnForeignGatewayBackend(vote.backend, ""); with agentBackend == "" that returns false (api_packs.go:1138), so a hive-default agent correctly vetoes rather than votes. BackendAllowed(shared) is checked before returning.
  • Both ./pkg/dashboard/ and ./crux/config/ build at the PR head. Changelog fragment name follows the fixed-<slug>.md convention.

Open question β€” does this reach the reporter's hive?

Only BackendIsOperatorOwned() backends vote (api_packs.go:1198-1200), and BackendOwner = FieldOwnerOperator is stamped in exactly three places, all per-agent dashboard edits: src/pkg/dashboard/api_agent_config.go:515, :993 and api_agent_control.go:207. Nothing in src/pkg/config/load.go or config.go stamps it on load. Every pack (L2–L4) declares backend: copilot for scanner/quality/guide, so those agents are on LiteLLM on the reporter's hive only because something overrode the pack. If that something was three per-agent edits, this PR fixes #14678. If it was a hive-level setting ("the dashboard's overall configuration" in the issue) that leaves BackendOwner empty, votes stays 0, hiveGatewayBackend returns "", and the reviewer stays on copilot β€” the PR would then land without changing the reported hive.

The tests in pack_gateway_model_test.go all construct the fleet with operator-owned backends, so they cannot distinguish these two cases. Worth confirming before merge; if the hive-level path is real, the vote probably needs to also consider the hive default backend when it resolves to a gateway.

I have not approved or requested changes; flagging for the author and a maintainer.

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

The mechanism is sound and I could not break the move itself β€” but the majority bar undercounts once the pack agents have followed, so the fix stops working at the next ACMM level up. One finding, then what I verified.

Finding (medium) β€” followers dilute their own majority, so the next pack agent lands back on copilot. src/pkg/dashboard/api_packs.go, hiveGatewayBackend: every enabled agent other than except increments others, but only BackendIsOperatorOwned() agents can increment votes; the gate is votes*2 <= others β†’ "". Agents this PR itself places on the gateway are created with BackendOwner: config.FieldOwnerPack (api_packs.go:379) or keep it when moved (the new block leaves existing.BackendOwner as pack). Walk the PR's own scenario one level further: operator moved scanner/quality/guide to litellm (3 votes); L2 creates reviewer on the gateway and moves supervisor/brainstorm there (3 pack-owned gateway agents). Raise to L3: for the new agent, others = 6, votes = 3, 3*2 <= 6 β†’ "", so gatewayPlacement returns "" and the L3 agent is created on the pack's copilot CLI β€” exactly the #14678 symptom, now for the agent added after the fix. On a hive with a governor/other unowned agent the dilution hits even earlier. Pack-owned agents already sitting on the shared gateway are evidence for the move, not against it; counting them in others while barring them from votes is the bug. One way out: treat a pack-owned agent whose backend EqualFolds shared as a vote (or exclude it from others), with a test that applies L2 then L3 on the gatewayHive fixture. TestApplyPackKeepsPackBackendWhenOperatorPicksAreMinority deliberately builds "three operator picks among six others" as the no-move case, so that test's framing would need to distinguish pack CLI agents (should dilute) from pack gateway agents (should not).

Verified at 38cd38c:

  • The move block cannot be undone later in the same apply: the "moving pack-owned agent off denied backend" branch (api_packs.go:287) only fires when !BackendAllowed(existing.Backend), and hiveGatewayBackend already rejects a gateway that is not BackendAllowed. Second apply is idempotent because existing.Backend no longer equals packBackend.
  • After the move, onForeignGateway (api_packs.go:208) becomes true, so packModel is cleared and the pack copilot id is never re-seeded β€” consistent with the #9945 handling.
  • IsCLIBackend / IsInferenceBackend / FieldOwnerPack exist at src/crux/config/agent_runtime.go:1287/1248/621.
  • BackendIsPackOwned is a straightforward sibling of BackendIsOperatorOwned (agent_runtime.go:647).

Body/diff mismatch, minor: the body says the gateway is the one every enabled agent runs on and lists "if any of them sits on a subscription CLI"; the code (and the changelog fragment) implement a majority of operator-owned picks, with pack-owned CLI agents explicitly non-vetoing. The changelog is the accurate one β€” worth aligning the body before merge.

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

…y majority

hiveGatewayBackend counted every other enabled agent in the majority bar
but only operator-owned backends as votes. The pack agents L2 moved onto
the gateway are pack-owned, so at L3 the three operator picks were three
votes among six others and the new ci-maintainer landed on copilot β€”
hivecommons#14678 one level up.

Operator picks still choose the gateway (any operator-picked CLI, hive
default or second gateway vetoes it); the majority is now every other
agent already on that gateway, whoever placed it there. Doc comment and
changelog describe the implemented rule.

Hive-Fix-Attempt: 3/6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kubestellar-prow kubestellar-prow Bot added dco-signoff: no Indicates the PR's author has not signed the DCO. and removed dco-signoff: yes Indicates the PR's author has signed the DCO. labels Oct 28, 2026
@kubestellar-prow

Copy link
Copy Markdown
Contributor

Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

πŸ“ Please follow instructions in the contributing guide to update your commits with the DCO

Full details of the Developer Certificate of Origin can be found at developercertificate.org.

The list of commits missing DCO signoff:

  • 591ce65 πŸ› packs: count agents already on the gateway toward the hive's gateway majority
Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@hivecommons-hive hivecommons-hive Bot added no-dco A commit is missing Signed-off-by β€” run `git rebase --signoff` and removed dco All commits are DCO signed-off labels Oct 28, 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 (correctness, delta 38cd38c→591ce65) — both previous findings are fixed.

  • Majority dilution: hiveGatewayBackend now picks the gateway from operator-owned backends only (api_packs.go:1201-1213) and then counts every other agent on it toward the bar (api_packs.go:1217-1228). For the L3 ci-maintainer, the three pack-owned agents L2 moved onto litellm count, so 6/6 passes; TestApplyPackGatewayHoldsAcrossLevels (pack_gateway_model_test.go:317-337) covers it. Dashboard test shards and coverage are green at this head.
  • PR body / changelog now match the implemented rule ("agents already on that gateway are a majority").

Advisory (non-blocking): because hiveBackends is snapshotted once before the apply (api_packs.go:152), a hive that got pre-fix L2 copilot agents and climbs to L3 on the same restart will see 3/6 on the gateway for ci-maintainer and create it on copilot pack-owned; the following restart moves it (api_packs.go:218-226). Converges in one extra restart, consistent with the body's "moved on the next restart".

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

@hivecommons-hive

Copy link
Copy Markdown
Contributor

@Karibhou this adopted PR still has unsigned commits, so the hive is re-homing it as #14777 on a hive-owned DCO-clean branch. Closing this PR so DCO does not stall the adopted work in place.

@hivecommons-hive hivecommons-hive Bot closed this Oct 28, 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=adjudicator-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-3 backend=copilot model=claude-fable-5.1 copilot=1.0.88

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 dco-signoff: no Indicates the PR's author has not signed the DCO. merge-method/merge Hive merges this PR with a merge commit (mixed authorship, each commit self-signed); never squash no-dco A commit is missing Signed-off-by β€” run `git rebase --signoff` role/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) size/L Denotes a PR that changes 100-499 lines, ignoring generated files. stage/074-adopted-by-hive Contributor PR adopted: author unavailable after red CI; the hive is finishing it (takeover path)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

πŸ› bug: reviewer agent has mysteriously inconsistent configuration for inference

1 participant