Repository navigation
π [v6] contributor-first: a relay busy for another hive does not cover this hive's lane - #17402
Conversation
β¦r this hive's lane A relay subscribed to several hives works one task at a time. While that task belonged to hive B, hive A still saw a connected relay holding its "Acting as" role with a fresh heartbeat and no refusal, so RoleCoverage counted it as contributor-first cover and A's resident agent stayed withheld while nobody worked A's lane. The relay now tells every other hub that advertises the new busy_elsewhere_v1 capability when it starts and finishes another hive's task (state busy/clear plus the task's start time; no hive, repo or issue). It restates on auth_ok and clears on complete, fail and revoke, since all of those end in writeRelayStatus. The hub records the flag per connection, clears it on the relay's next ready, and RoleCoverage skips a busy-elsewhere connection that holds no task or fix-pr task from this hub. No wall-clock expiry: the flag dies with the connection, and a stale flag can only fall back to the resident agent working the lane. Old relays and old hubs keep today's behaviour. 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>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Read the diff against v6 plus the relay and hub paths it hooks into. This looks correct to me; nothing blocking.
What I verified:
- Hub flag lifecycle.
handleBusyElsewhere(contribute_ws.go:3313) only setsbusyElsewhere;busyElsewhereLocked(contribute_coverage.go:215) requirescurrentTask == nil && len(c.fixTasks) == 0, so a relay this hub has also handed work still counts.handleReadyclears the flag under the samec.muhold ascurrentTask/pendingExternalTask(contribute_ws.go:3744), so a lostclearframe cannot park a relay that is back asking this hub. Mirrors thequota_holdshape exactly. - Relay side.
_hubis set on everytask_assign(contributor-relay.js:10055base) beforewriteRelayStatus(), and the complete/fail/revoke paths that nullcurrentTask(base5310,8345,8594,10190) all end inwriteRelayStatus(), soreportBusyElsewhereToHubs()sees each transition. The onecurrentTask = nullthat does not (shutdown, base10452) drops the socket, and the flag dies with the connection β consistent with the "no timer" argument inbusyElsewhereLocked's comment. - No false
readywhile busy.requestWorkrefuses to sendreadywhencurrentTaskis set (contributor-relay.js:2444), so the ready-clears-flag rule on the hub cannot be tripped by the busy relay itself. - Frame carries only
state/since;sinceis bounded to 40 chars before logging. No hive/repo/issue leaks between hubs.
On the design question in the body: option A (no automatic role-hive preference) is what the diff implements and it is the hive's own call, not a risk class β I'd leave it as-is.
β hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88
|
Hive status β updated in place; edits do not notify. Full history is in the Activity Monitor. Review findings from Read the diff against the tree at What I verified:
On the design question in the body: option A (no automatic preference) is a hive engineering call and the PR leaves behaviour unchanged there; nothing in this diff depends on it.
|
Summary
A contributor relay subscribed to several hives works one task at a time. While it works hive B's task, hive A still sees a connected relay that holds its "Acting as" role, answers heartbeats and has been refused nothing, so A counts it as contributor-first cover and keeps its resident agent withheld while nobody works A's lane. With this PR the relay tells its other hives when it starts and finishes another hive's task (new
busy_elsewhere_v1capability), and the hub leaves such a relay out of coverage. Old relays and old hubs behave exactly as before.What went wrong
Seen on 2026-11-09 with Danathar's relay, which subscribes to four hubs:
ready for workat 14:24:37Z (answeredno_matching_work), owner set "Acting as: scanner" at 14:24:57Z, and the nextreadyonly at 14:32:26Z.projectbluefin happened not to count the relay as cover, only because its one earlier
readyhad been answeredno_matching_work, which started the two-minute idle run from #17284. If the relay's first action had been to take another hive's task, projectbluefin would have counted it for the whole seven minutes.Root cause:
RoleCoverage(src/pkg/dashboard/contribute_coverage.go) has no way to know a relay is busy for another hub, and the relay never says so.What changes
bin/contributor-relay.js): sends{type: "busy_elsewhere", state: "busy", since}to every hub that advertisesbusy_elsewhere_v1, except the hub that owns the task, andstate: "clear"when that task completes, fails or is revoked. All of those already end inwriteRelayStatus(), which is where the quota-hold report (π [v6] contributor-first: a relay that is holding work does not cover a laneΒ #17233) runs too. It restates onauth_ok. The frame never names the other hive, repository or issue. The local PR-review cycle belongs to no hub and is not reported.busy_elsewhere_v1, records the flag on the connection, logs each change, and clears it on the relay's nextready, since a relay asks only when it holds no task.RoleCoverageskips a busy-elsewhere connection, in the same way it skips standby and quota hold, unless this hub has also handed it a task or a fix-pr task.Why no timer on the hub: the flag lives on the connection, so a socket close or relay restart drops it. The relay clears it when the task ends, and a
readyclears it as well. A wall-clock expiry would start counting a relay that is still busy on a long task again, which is the bug this PR fixes. A flag left set by mistake can only make the resident agent work the lane, which is what happens withoutcontributor_first.Design question: should a relay prefer the hive where it holds a role?
This PR does not change it. When choosing which hive to ask next, the relay could favour the hive where it is "Acting as" a role.
busy_elsewherekeep coverage honest. With the defaultrankedstrategy, a contributor who wants the role hive first can already list it first inHIVE_HUB.role-first). A possible follow-up, but it needs a new hub-to-relay frame: today the relay learns about an owner's "Acting as" assignment only through a free-textnotice, and its ownHIVE_AGENT_ROLEapplies to every hub.I recommend A. Contributor-first exists so the lane falls back correctly when a relay is not working it. It was not meant to override where a volunteer chose to spend their tokens. B is not a one-line change, so I have left it out.
Files
bin/contributor-relay.js,bin/contributor-relay.test.jssrc/pkg/dashboard/contribute_coverage.go,contribute_ws.go,contribute_protocol.go,contribute_coverage_test.gosrc/pkg/governor/contributor_first.go(comment only)src/docs/agent-configuration.md(coverage rules),src/docs/contributor-relay.md(newbusy_elsewhere_v1section),src/docs/design/agent-state-inventory.md(line citation moved by the new fields)changelog.d/fixed-busy-elsewhere-relay-not-cover.mdTests
I did not run these locally, because the repository's agent rules leave that to CI. Only
gofmt -landnode --checkwere run.TestRoleCoverage_BusyElsewhereRelayDoesNotCover: its first assertion records today's behaviour: a role-holding relay that has sent noreadyand no report is counted as cover. Then abusyreport drops it andclearrestores it. On the base commit there is no message or flag that can remove the relay from coverage, so the second assertion cannot pass there.TestRoleCoverage_BusyElsewhereWithThisHubsTaskStillCovers: a relay that this hub has also handed a primary task or a fix-pr task still counts.TestRoleCoverage_ReadyClearsBusyElsewhere: a realreadythroughhandleReadyclears the flag, so the relay counts again.TestServerCapabilities_AdvertisesBusyElsewhere.busy_elsewhere_v1tests check that a task from hive B is reported to A only, with no task details, restated on A's reconnect and cleared on completion; that a revoke clears it; and that a hub without the token sees no frame.Refs #17284, #17233
π€ Generated with Claude Code
https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1