Skip to content

πŸ› fix: refuse automated claims on needs-human issues - #10533

Closed
mendezr wants to merge 3 commits into
hivecommons:v5from
mendezr:fix/10526-needs-human-claim
Closed

mendezr wants to merge 3 commits into
hivecommons:v5from
mendezr:fix/10526-needs-human-claim

Conversation

@mendezr

@mendezr mendezr commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #10526.

  • Refuse non-human ledger claims, renewals and forced takeovers when current issue labels include needs-human or a hold label. Human claims remain allowed; failed forge lookups cannot admit automated claims.
  • Apply the same check to issue-request claims and relay claim comments.
  • Release existing blocked automated claims on the 30-second cleanup tick through the usual release hooks. Preserve human claims, failed lookups and concurrent takeovers.
  • Preserve the shared enumerator's existing needs-human exclusion from kick/contributor work lists, with explicit regression coverage.

Validation

  • Added regression tests for claim refusals, renewal/takeover refusal, cleanup releases, concurrent human takeover, lookup failures, kick wiring, relay markers and actionable-list exclusion.
  • gofmt -l on changed Go files and git diff --check: clean.
  • Tests not run locally, per AGENTS.md; CI is the test verdict.

Traceability

Plan: needs-human-claim-gate. Spec fallback: issue-10526#acceptance (the issue's acceptance criteria; no separate plan/spec identifiers were supplied).

β€” hive: backend=pi model=openai-codex/gpt-6.1-sol


🐝 Hive Agent: contributor | SHA: f74ed3606

Gate automated ledger claims, renewals and takeovers on current GitHub labels. Apply the same gate to claim requests and relay claim markers, and release blocked automated claims on the cleanup tick while preserving human claims and concurrent takeovers.

Hive-Run: hivecommons#10526
Hive-Plan: needs-human-claim-gate
Hive-Spec: issue-10526#acceptance
Signed-off-by: mendezr <mendezr@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 4, 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 kellyaa 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 4, 2026
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Open PR overlap check

Base branch: v5

This PR conflicts with v5 itself. Rebase it first: until it merges cleanly into the base, conflicts with other open PRs cannot be told apart from base drift, so only same-file overlaps are listed.

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.

PR Title Relation Files
#10751 πŸ› fix: refuse automated claims on needs-human issues (takeover) same files changelog.d/fixed-needs-human-issue-claims.md
src/cmd/hive/claims_admission_test.go
src/cmd/hive/claimswire.go
src/cmd/hive/main.go
src/pkg/claims/admission_test.go
src/pkg/claims/claims.go
src/pkg/dashboard/blocked_claims_loop_test.go
src/pkg/dashboard/contribute_claim_gate_test.go
src/pkg/dashboard/contribute_claims.go
src/pkg/dashboard/contribute_ws.go
src/pkg/github/issue_claim_gate.go
src/pkg/github/issue_claim_gate_test.go
src/pkg/github/issue_request_watcher.go
src/pkg/github/issue_request_watcher_test.go
src/pkg/github/repo_pause_test.go

@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 the ledger, the watcher and the cleanup loop against v5. The gate itself looks correct to me: Claim reads l.admit under the lock and runs the check outside it (src/pkg/claims/claims.go:338-350), ReleaseBlocked only deletes the exact snapshot it checked (claims.go:496-500, so a concurrent human takeover survives), lookup errors preserve claims, and KindHuman is skipped on both paths. The watcher's break on a lookup error lands in the existing retry branch (src/pkg/github/issue_request_watcher.go:560-575) rather than claiming β€” good. The diff does what the body says.

One concern worth a decision before merge (medium): ReleaseBlocked does one Issues.Get per live non-human claim every 30 seconds, synchronously on the cleanup goroutine.

  • src/pkg/dashboard/contribute_ws.go:4190 β€” cleanupLoop ticks every 30 * time.Second; the new call at contribute_ws.go:4224 runs on that goroutine, before the stale-websocket reap that follows it.
  • src/pkg/claims/claims.go:487-491 β€” the loop over l.List() calls check(...) for every non-human claim, and the installed check (src/cmd/hive/claimswire.go:66-68) is a live IssueClaimBlockReason β†’ Issues.Get with a 15s timeout each.

Consequences: (a) request volume scales as claims Γ— 120/hour β€” 40 live agent/contributor claims is ~4,800 GETs/hour from this loop alone, against an App installation budget of 5,000/hour, and it runs even when nothing has changed; (b) under a slow or rate-limited GitHub, each call can block up to 15s serially, so a tick with N claims can delay stale-connection cleanup by up to 15sΓ—N β€” the comment block directly below (contribute_ws.go:4225-4235) explains why that goroutine was deliberately kept fast.

Nothing in the issue requires a 30s cadence β€” #10526 says "on the next watcher tick". Options a maintainer could pick: run the recheck on its own, slower ticker (e.g. every few minutes), only recheck claims past some age, or move it off the cleanup goroutine. Any of these keeps the fix; I just wouldn't land it on the 30s connection-reaper tick as-is.

Question for a human: is the per-claim GitHub poll every 30s acceptable for this hub's claim volume, or should the recheck get its own cadence? @Danathar has touched the claim ledger wiring recently and is probably best placed to decide.

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

@hivecommons-hive

Copy link
Copy Markdown
Contributor

CI check report (comment-only β€” this PR's head lives in a fork, so the hive cannot push to it).

Failing: test (rest 4/5) β†’ pkg/github TestIssueRequestWatcher_RepoPause/claim, 3 runs / 3 failures (job 111513642601). PR-local β€” v5 is green and no sibling PR fails this test.

Cause: the new needs-human gate in issue_request_watcher.go now does GET /repos/o/r/issues/42 to read the issue's labels before honouring a claim request. The stub server in src/pkg/github/repo_pause_test.go (~L217-230) only routes GET /repos/o/r/issues, GET /repos/o/r/labels/* and POST <tc.endpoint>; everything else hits the default: branch, which fails the test and returns 404, so the gate errors with checking issue claim labels: ... 404 and the resumed repo records 0 writes instead of 1.

Fix (one case in the stub switch, before default:):

case r.Method == http.MethodGet && r.URL.Path == "/repos/o/r/issues/42":
	_, _ = io.WriteString(w, `{"number":42,"labels":[]}`)

(Only the claim subtest exercises it; comment never hits that path, so a shared case is fine. If issueTestClient counts calls, the extra GET does not affect the writes assertion at L281.)

Everything else is green; once that lands the shard should pass on the next push.


🐝 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

Signed-off-by: mendezr <mendezr@users.noreply.github.com>
Signed-off-by: mendezr <mendezr@users.noreply.github.com>
@mendezr

mendezr commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the scanner fixture failure and reviewer polling concern in f3a6127 / 825bf8d: the issue-label GET is now stubbed, and blocked-claim rechecks run on a separate, non-overlapping five-minute ticker (12 reads/claim/hour) instead of blocking the 30-second connection reaper. Added release/shutdown regression coverage. Targeted watcher, claims, and dashboard tests pass locally; Podman is unavailable in this environment.


🐝 Hive Agent: contributor | SHA: unknown

@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 claims.go, issue_claim_gate.go, issue_request_watcher.go, contribute_claims.go, contribute_ws.go, claimswire.go and the callers in claims_api.go / contribute_leases.go at head 825bf8d. The gate itself is implemented carefully β€” ReleaseBlocked only deletes the exact snapshot it checked (claims.go:496–500), human claims bypass everywhere, and the issue-request path retries rather than admits on lookup error (issue_request_watcher.go:447). One medium-severity consequence of the fail-closed choice that I think needs a decision before this lands, plus a body/code mismatch.

Medium: renewals are now fail-closed, so a GitHub outage silently expires live contributor claims β€” claims.go:339–348, claims_api.go:151, contribute_leases.go:604–608

Ledger.Claim runs the admission check for every non-human request, including the renewal that renewClaimForLease issues alongside each lease renewal. On err != nil it returns before expireLocked/the expiry extension (claims.go:343–345), so the existing claim is not renewed. Before this PR, renewal was a purely local ledger operation. Consequence: if the Issues.Get lookup keeps failing (GitHub degraded, secondary rate limit, or b.ghClient == nil β†’ ErrNoGitHubClient at issue_claim_gate.go:14) for longer than the contributor claim TTL (30m per the comment at contribute_leases.go:605), a contributor who is actively working under a valid lease loses their ledger claim and the issue becomes claimable by agents and other hives. The ledger is authoritative for the hub's own decisions, so this is a real collision window, and it only appears during the exact conditions where the hub is already struggling.

The body's stated invariant β€” "failed forge lookups cannot admit automated claims" β€” is right for new claims, but a renewal doesn't admit anything; the holder already holds it. ReleaseBlocked already handles label changes on existing claims and correctly treats lookup failure as "preserve" (claims.go:491–493). Suggestion: in Claim, when the check errors and the ledger already holds this key for the same HolderID, fall through to the normal renewal path instead of returning the error (or skip the check entirely for same-holder renewals and let ReleaseBlocked be the enforcement for existing claims). That keeps fail-closed for new claims and fail-safe for renewals.

Low: kick-time agent claims now do one blocking GitHub GET per issue, serially β€” claimswire.go:66–68, :219–224
recordAgentKickClaims loops over the kick's issue refs calling ledger.Claim, each of which now performs a synchronous Issues.Get with a 15s timeout. A kick naming N issues can spend up to 15Β·N seconds here under GitHub slowness. Probably acceptable for typical kick sizes, but worth knowing; the previous code was in-memory only.

Info: body/code mismatch on the recheck cadence β€” contribute_ws.go:4186–4204
The body says blocked claims are released "on the 30-second cleanup tick"; the code at this head starts a separate blockedClaimsLoop(5 * time.Minute) from cleanupLoop. The 5-minute separate loop is the better design (the comment explains why), but the description should say so.

Open question (not a finding): is the claims ledger expected to function in a hive with governor.claims.enabled: true but no GitHub App configured? If so, installClaimAdmissionCheck turns every automated claim into ErrNoGitHubClient; if not, fine.

Fork PR, so I cannot push a fix; flagging for @mendezr and a maintainer familiar with the claims ledger (#8380 lineage).

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

@hivecommons-hive

Copy link
Copy Markdown
Contributor

CI check report (comment-only β€” this PR's head lives in a fork, so the hive cannot push to it).

Failing: coverage β†’ COVERAGE GATE FAILED: github: 88.9% (floor 89%) (job 111894199575). All other checks, including the test (rest 4/5) shard from the previous report, are green now.

Diagnosis: pkg/github misses its 89% floor by 0.1 pt. The production delta in this PR is tiny (issue_claim_gate.go plus the lookup/deny branch in issue_request_watcher.go) and issue_claim_gate_test.go covers it, so the shortfall is almost certainly the union with v5: this head is 165 commits behind v5 (compare/v5...mendezr:hive:fix/10526-needs-human-claim β†’ behind_by 165), and the gate measures the merge result.

Fix:

  1. git fetch upstream v5 && git merge upstream/v5 (or rebase) and push β€” re-measure first.
  2. If it is still a hair short, two cheap uncovered lines in the PR itself: the err != nil β†’ break retry path in issue_request_watcher.go (stub GET /repos/o/r/issues/42 returning 500 once) and the hold branch in IssueClaimBlockReason (issue labelled hold). Either one clears 0.1 pt.

🐝 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

@clubanderson

Copy link
Copy Markdown
Member

Thanks again for the fix here, @mendezr. I rebased and carried it forward in #10751 with your commits and authorship preserved, then added a small v5 conflict/coverage follow-up. We'll leave this PR open for now and close it once the takeover PR merges.

@clubanderson

Copy link
Copy Markdown
Member

Closed in favor of the merged takeover PR #10751: #10751. Thanks again @mendezr β€” your commits were carried forward with authorship preserved.

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

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[fix] claim gate: refuse to claim issues labelled needs-human

2 participants