feat(agent-actions): add an install-wide contributor open-item cap across repos#2649
feat(agent-actions): add an install-wide contributor open-item cap across repos#2649JSONbored wants to merge 8 commits into
Conversation
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-03 05:23:52 UTC
⏸️ Suggested Action - Manual Review
Review summary Blockers
Nits — 6 non-blocking
Concerns raised — review before merging
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2649 +/- ##
=======================================
Coverage 96.10% 96.11%
=======================================
Files 237 237
Lines 26540 26606 +66
Branches 9625 9645 +20
=======================================
+ Hits 25507 25573 +66
Misses 424 424
Partials 609 609
🚀 New features to boost your workflow:
|
cb18e14 to
bb8ff58
Compare
…ross repos contributorOpenPrCap/contributorOpenIssueCap only see one repo, so an actor spreading low-volume spam across several repos on the same self-host install never trips any single repo's own cap. Adds an optional GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP env var (install-scoped, not per-repo, mirroring ADMIN_GITHUB_LOGINS) that sums an author's open PRs + open issues across every repo this install gates, checked only when the per-repo cap didn't already match. Reuses the existing contributor_cap closeKind and autoCloseExemptLogins list. Off by default. Closes #2562.
…sing The global aggregate trusted the stored DB cache directly, so a stale open row from another repo (closed on GitHub after this instance's cache went stale) could inflate the count and trigger a wrongful irreversible close -- the existing per-repo issue cap already avoids this exact failure mode via sibling live-verification. Adds listOpenItemsByAuthorAcrossInstall (rows, not just a count) and verifiedGlobalOpenItemCount, which live-confirms every OTHER contributing item before trusting it toward the cap. Also fixes the close/audit message mislabeling a combined PR+issue total as a single kind by widening itemKind to "pull requests and issues". Addresses gate review findings on #2562.
A global-cap close said "this repository's configured limit" even though the count spans every repo the install gates -- misleading since the limit isn't scoped to the one repo being evaluated. Addresses a gate review finding on #2562.
listOpenItemsByAuthorAcrossInstall queried pullRequests/issues with no installationId scoping at all, despite its own doc comment asserting every row already belonged to a repo the calling install gates. Since those tables key by repoFullName (a plain string, not an FK), a contributor with open items on a DIFFERENT installation's repo could leak into this installation's cap count. Scope the query through repositories.installationId first (matching the existing markRepositoriesRemovedFromInstallation precedent), then inArray(...) against the resulting repoFullNames -- this codebase has no Drizzle joins to lean on instead.
…allback codecov/patch flagged one uncovered branch: verifiedGlobalOpenItemCount's own installation-token mint failing and falling back to GITHUB_PUBLIC_TOKEN for the cross-repo live-verification GET calls (src/queue/processors.ts:3958). Mirrors the existing coverage for the same fallback pattern on the per-repo issue-cap path, but exercises the GLOBAL check's own token mint specifically.
48a1723 to
82ef382
Compare
…test GITHUB_PUBLIC_TOKEN: "public-fallback-token" (22 chars) matched the gate's generic-secret-assignment heuristic. Switch to the file's existing "public-token" convention (12 chars, already used by every other GITHUB_PUBLIC_TOKEN fixture here), which is short enough to fall under the pattern's 16-char threshold.
verifiedGlobalOpenItemCount ran an unbounded Promise.all over every open row across the install (up to 4000, since the two source queries each cap at 2000) -- a single contributor webhook could fire that many concurrent GitHub API calls and exhaust the installation's rate limit for every other repo it gates. Cap the live-verification batch to 10 concurrent requests via a small worker-pool helper, mirroring the same pattern already used for GitHub API fan-out in src/github/backfill.ts (duplicated locally rather than shared, matching that file's own precedent of not centralizing it). Addresses a security-scan finding on #2562.
…, not silent listRepoFullNamesForInstallation and listOpenItemsByAuthorAcrossInstall used fixed LIMITs (5000 repos, 2000 PRs, 2000 issues) that silently dropped rows once hit -- violating the feature's own contract that every repo in the install is counted, and letting an install or author above those limits undercount toward (and bypass) GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP with no signal anything was truncated. Raises both limits to 20,000 (far beyond any realistic install/author scale) and, on the rare install where a limit is still hit, records an audit event (agent.global_open_item_cap.repo_list_truncated / .author_items_truncated) instead of returning a truncated result with no trace. The live-verification fan-out this feeds is already bounded to a fixed worker pool (a prior commit on this branch).
|
Superseded — issue #2562 was independently implemented and merged via #2678 while this PR was in flight (both were built in parallel during the same campaign). Closing this one rather than duplicating already-shipped work. The gate's own review on this PR caught a real cross-installation scoping bug in my implementation before I noticed #2678 had landed. That same bug exists in #2678's shipped version too (it counts an author's open items with no installation scoping at all) — filed and fixed directly as #2687. |
Summary
contributorOpenPrCap/contributorOpenIssueCaponly see one repo — a self-host operator gating multiple repos on one install has no fleet-wide view: an actor spreading low-volume spam/farming PRs or issues across several gated repos never trips any single repo's own cap, even though their fleet-wide open-item count is high.GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAPenv var — install-scoped, not a per-repo.gittensory.yml/repository_settingsfield, mirroringADMIN_GITHUB_LOGINS's own bare-env-var shape (no new DB table/migration needed). Off by default.countOpenItemsByAuthorAcrossInstallquery, checked only when the existing per-repo cap didn't already match (no redundant query once a close is already decided).contributor_capcloseKind/close-message shape the per-repo cap already uses, and the sameautoCloseExemptLoginsexemption list the review-nag cooldown already uses.Closes #2562.
Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlint(vianpm run test:ci)npm run db:migrations:check— no new migration (env-var config, not a DB-backed setting)npm run typechecknpm run test:coveragelocally — tests covering: an actor over the install-wide cap but under every individual repo's cap is caught (PR and issue paths); off-by-default (env var unset);autoCloseExemptLoginsexempts the author on both the PR and issue paths; a below-generous-cap actor is not closed; the global check is skipped once the per-repo check already matched (asserted via which cap number appears in the close comment); a blank/non-numeric/non-positive env var all behave as off; an author-less (ghost) PR is excluded, not crashed on; a direct unit test oncountOpenItemsByAuthorAcrossInstall(cross-repo sum, closed/other-author exclusion, case-insensitivity).npm run test:workers(vianpm run test:ci)npm run build:mcp(vianpm run test:ci)npm run test:mcp-pack(vianpm run test:ci)npm run ui:openapi:check— no schema change (install-wide, not aRepositorySettingsfield, so intentionally not surfaced in the OpenAPI schema, mirroring the existing global-blacklist singleton's own DB-only, no-route precedent)npm run ui:lint(vianpm run test:ci)npm run ui:typecheck(vianpm run test:ci)npm run ui:build(vianpm run test:ci)npm audit --audit-level=moderateSafety
UI Evidencesection below with screenshots. — N/A, no UI change..gittensory.yml-documented setting.Notes