Repository navigation
π [v6] contributor-first: a relay at its task limit does not cover a lane - #17268
hivecommons-hive[bot] merged 2 commits into
Conversation
β¦lane RoleCoverage counted a relay as covering its role even when task selection refused it work because its identity had used up its tier's max_per_hour or max_per_day. The relay stays connected and is refused every ready with hourly_limit / daily_limit until the window rolls, so the governor kept the resident agent's kicks withheld while nobody worked the lane. Same class as the standby / quota-hold gaps hivecommons#17233 handled. - Factor the hourly/daily rule out of selectTaskPass into tierRateLimitReached (hub.tier_limits[tier], caps <= 0 unlimited, hourly checked first). Selection keeps its log lines, decision text and refusal reasons. - RoleCoverage skips a candidate whose identity is at either cap, using the same function, after h.mu is released (rateMu only; no selectMu). - Docs: RoleCoverage comment, governor contributor-first header, and the contributor-first coverage list in agent-configuration.md. 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 |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of
|
|
Hive status β updated in place; edits do not notify. Full history is in the Activity Monitor. Evidence bundle `#172684cc43be` sha256:932b66842d609ce91ab4813df501a1dca6019067726b9884bbe19c0c58d1be57 β download from the dashboard or `hivectl review evidence #17268`.Reviewed by Reviewer notesRead the tree at merge-base Behaviour preserved in selection. The old inline hourly/daily block in Coverage uses the same inputs. Tests cover hourly, daily, under-limit (including a rolled-off 25h stamp and a mixed-tier lane) and the shared function directly, and No findings. |
β¦ing work does not cover a lane (#17284) RoleCoverage counted a relay as covering its role when the hub would not hand it work (revoked, or its trust tier in hub.disabled_tiers) and when every ready it sent was answered no_matching_work. Both stay connected, so the governor kept the resident agent's kicks withheld while nobody worked the lane. Seen on hive-wild-mole on 2026-11-08: a relay covered scanner while selection answered no_matching_work every 30 s. Same class as #17233 and #17268. - Factor revoked / disabled tier into tierAccessGateReason (disabled half: tierDisabled). resumeGateReason keeps its reasons and order; fresh selection's disabled_tiers loop now calls tierDisabled. RoleCoverage reads the tier under c.mu and evaluates with h.mu released. - Record the idle run on the connection: lastIdleReasonSince beside lastIdleReason, set on task_unavailable in the ready and yank paths, no_matching_work and repo_unmintable folded into one run. Cleared on the selection commit, standby dispatch, resume adoption and sendFixTask. - RoleCoverage skips a relay holding no task that has been answered no_matching_work / repo_unmintable for coverageNoWorkWindow (2 min from the first answer; the relay retries every 30 s and the governor still applies contributor_first_grace on top). - Docs: RoleCoverage comment, agent-configuration.md coverage list, governor header, one drifted citation; changelog fragment. Claude-Session: https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1 Signed-off-by: Danathar <Danathar@users.noreply.github.com> Co-authored-by: Danathar <Danathar@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: hivecommons-hive[bot] <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Summary
A contributor relay that has used up its hourly or daily task allowance still counted as covering its contributor-first lane. The hub refuses that relay work until the window rolls, so the governor kept the resident agent's kicks withheld while nobody worked the lane: up to an hour for the hourly cap, up to most of a day for the daily one. Coverage now leaves such a relay out. It uses the same limit check that task selection refuses work with, now factored into one function both call, so the two can't drift apart.
This is the same class of bug as the standby and quota-hold gaps that #17233 handled in
RoleCoverage. Refs #17233.What went wrong
RoleCoverage(src/pkg/dashboard/contribute_coverage.go) counts a relay as cover when its role, heartbeat, failure-streak, standby and quota-hold state say it is taking work. It never looked at the per-tier rate limits. Task selection (selectTaskPassincontribute_select.go) refuses a relay withhourly_limitordaily_limitonce its identity has been handedhub.tier_limits.<tier>.max_per_hourtasks in the last hour ormax_per_dayin the last 24 hours. So a relay at, say, 10 of 10 for the day was refused everyready, but still reported as covering, and the resident agent stayed idle until the window rolled.What changes
tierRateLimitReached(identity, tier, now)incontribute_select.goholds the hourly/daily rule that used to be inline inselectTaskPass. It returns which cap was hit plus the count and the cap. Selection now calls it and keeps its exact log lines, decision text and refusal reasons.RoleCoverageskips a candidate whose identity is at either cap. The check runs afterh.muis released, next to the failure-streak check, so no lock is nested insideh.mu.RoleCoveragedoc comment, the governor's contributor-first header comment, and the "Contributor-first lanes" list insrc/docs/agent-configuration.mdnow name the rule. They also say that a relay holding itsmax_concurrenttasks is busy and still counts.How selection gets the limits, which the shared function keeps as-is:
hub.tier_limits[<trust tier>]and nothing else. Config load fills in the shipped row for each built-in tier the operator didn't set (config/defaults.go). There is no per-contributor override (ContributorProfile.RateLimitsis stored but never enforced). A tier with no row, or a cap of 0, is unlimited. The hourly cap is checked first.Locking and cost. The count comes from
rateWindowCounts, which takes onlyrateMu. That lock is never held acrossh.mu, and coverage never takesselectMu. It scans at most one day of assignment stamps for one identity, once per candidate relay per governor evaluation.RoleCoverageis called once per eval cycle (cmd/hive/main_eval_cycle.go). The config is read unlocked, the same way selection andcontributionsSuspendedread it.Files
src/pkg/dashboard/contribute_select.go: newtierRateLimitHit/tierRateLimitReached; selection calls it.src/pkg/dashboard/contribute_coverage.go:RoleCoverageskips a relay at its cap; doc comment.src/pkg/dashboard/contribute_coverage_test.go: new tests.src/pkg/governor/contributor_first.go: header comment only, no gate logic.src/docs/agent-configuration.md: contributor-first coverage list.changelog.d/fixed-limit-reached-relay-not-cover.mdTests
New, in
contribute_coverage_test.go:TestRoleCoverage_HourlyLimitReachedDoesNotCover: at the hourly cap, real selection refuses withhourly_limitand the relay doesn't cover.TestRoleCoverage_DailyLimitReachedDoesNotCover: at the daily cap with nothing in the last hour, selection refuses withdaily_limitand the relay doesn't cover.TestRoleCoverage_UnderRateLimitsCovers: a relay one short of both caps still covers, and a stamp older than 24h doesn't count. A same-tier relay at the hourly cap in the same lane is the only one dropped.TestTierRateLimitReached: table test of the shared rule (under, hourly, daily, both report hourly, rolled-off stamps, zero caps, a tier with no row, no server, nil hub).On v6 tip
fd10c75e5the threeRoleCoveragetests fail: the at-cap relay is reported as cover. Their setup checks, which ask real selection and see it refuse, pass on the tip, which shows the mismatch directly.TestTierRateLimitReacheddoesn't build on the tip because the function is new. All four pass with the change, along with the existingTestRateLimits_*selection tests.Ran in full:
go test ./pkg/dashboard/... ./pkg/governor/...andgo veton both. Docs citation, docs link and api-reference citation checks pass.π€ Generated with Claude Code
https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1