Skip to content

feat(agent-actions): add an install-wide contributor open-item cap across repos - #2678

Merged
JSONbored merged 1 commit into
mainfrom
feat/install-wide-contributor-open-item-cap
Jul 3, 2026
Merged

feat(agent-actions): add an install-wide contributor open-item cap across repos#2678
JSONbored merged 1 commit into
mainfrom
feat/install-wide-contributor-open-item-cap

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Adds an optional, install-scoped GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP env var (src/env.d.ts, parsed by the new src/settings/global-contributor-cap.ts) so a self-hosted install that gates multiple repos can cap a contributor's SUM of open PRs+issues across every repo it tracks, not just one repo. Off by default (unset/invalid ⇒ no cap), mirroring global_contributor_blacklist's tenant-free-singleton design (src/db/repositories.ts:589 getGlobalContributorBlacklist) but as an env var since the issue doesn't need a maintainer-editable value, just an install operator's own config.
  • Adds countOpenItemsForAuthorAcrossRepos (src/db/repositories.ts), a same-database aggregate query summing open PRs + open issues for one author with no repoFullName filter, unlike the existing countOpenPullRequests/countOpenIssues. No cross-instance networking — purely a query against the same D1/SQLite database every repo on the install already shares.
  • Wires the install-wide check into both existing contributor-cap call sites in src/queue/processors.ts (runAgentMaintenancePlanAndExecute for PRs, maybeCloseIssueOverContributorCap for issues), IN ADDITION TO the existing per-repo contributorOpenPrCap/contributorOpenIssueCap checks, in the same short-circuit block. Reuses the existing autoCloseExemptLogins shared exemption list (src/settings/auto-close-exempt.ts) so a maintainer-named login is exempt from the install-wide cap exactly like the per-repo caps and review-nag cooldown.
  • Reuses the existing contributorCapMatch/closeKind: "contributor_cap" short-circuit in src/settings/agent-actions.ts — no new disposition type. Added an optional scope?: "repository" | "install" field so the close-comment wording is accurate ("this repository's configured limit" vs. "this install's configured limit (across every repository it gates, combined)") without changing the closeKind, label, or overall message shape; absent scope defaults to the original repository wording for back-compat with every existing caller.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally; codecov/patch requires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

Ran the full npm run test:ci gate (actionlint, migrations check, cf-typegen check, typecheck, full unsharded coverage suite, workers tests, MCP build/pack, miner build, REES tests, UI openapi/settings-parity/version-audit/lint/typecheck/test/build) — all green, 354 test files / 6751 tests passed, 0 failures. npm audit --audit-level=moderate reports 0 vulnerabilities.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests.
  • API/OpenAPI/MCP behavior is updated and tested where needed. (No public API/OpenAPI surface changed — this is an internal maintenance-action input, so no OpenAPI regen was required.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. (N/A — no UI changed.)
  • Visible UI changes include a UI Evidence section below with screenshots. (N/A — backend-only change, no visible UI.)
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. (No CHANGELOG.md edit.)

UI Evidence

N/A — this is a backend-only change (no UI surfaces touched).

Notes

  • The cap is deliberately an env var (GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP), not a per-repo .gittensory.yml/DB field, because it aggregates ACROSS repos and therefore cannot be "this repo's" setting — it belongs to the install as a whole. It is not added to wrangler.jsonc's hosted vars block (mirrors how other self-host-oriented numeric env vars like AI_BYOK_DAILY_REPO_LIMIT are handled — declared only in src/env.d.ts), so it does not require cf-typegen regeneration.
  • Left out of scope: no maintainer-facing settings-row UI for this cap (an install operator sets the env var directly), since the issue explicitly frames this as "likely an env var" and off-by-default is the hard requirement, not a maintainer dashboard control.

Closes #2562

…ross repos

A self-hosted install that gates multiple repos shares one database, but the
per-repo contributorOpenPrCap/contributorOpenIssueCap only ever count open
items on the same repo, so an actor spreading low-volume spam/farming PRs
across several gated repos in one install never trips any single repo's cap.
This adds an optional GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP env var, checked in
addition to (not instead of) the existing per-repo caps via a same-database
aggregate query over every repo the install tracks -- no cross-instance
networking, off by default, reusing the existing contributor_cap closeKind
and close-message shape (mirrors global_contributor_blacklist's install-scoped
singleton pattern).

Closes #2562
@dosubot dosubot Bot added the size:L label Jul 3, 2026
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.11%. Comparing base (642b12d) to head (41637eb).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2678   +/-   ##
=======================================
  Coverage   96.10%   96.11%           
=======================================
  Files         237      238    +1     
  Lines       26538    26563   +25     
  Branches     9624     9634   +10     
=======================================
+ Hits        25505    25530   +25     
  Misses        424      424           
  Partials      609      609           
Files with missing lines Coverage Δ
src/db/repositories.ts 96.60% <100.00%> (+<0.01%) ⬆️
src/queue/processors.ts 92.63% <100.00%> (+0.05%) ⬆️
src/settings/agent-actions.ts 94.32% <100.00%> (+0.04%) ⬆️
src/settings/global-contributor-cap.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored JSONbored self-assigned this Jul 3, 2026
@JSONbored
JSONbored merged commit 0e85cee into main Jul 3, 2026
11 checks passed
@JSONbored
JSONbored deleted the feat/install-wide-contributor-open-item-cap branch July 3, 2026 05:18
JSONbored added a commit that referenced this pull request Jul 3, 2026
countOpenItemsForAuthorAcrossRepos (merged via #2678, closing #2562)
counted an author's open PRs/issues across the ENTIRE D1 database with
no installation scoping at all -- on a database shared by multiple
installations (the hosted product's normal shape, and possible on
self-host too), a contributor's activity on one installation could
wrongly trigger GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP closes on a completely
unrelated installation that never gated them.

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.

Also fixes 5 AiReviewCacheInput test fixtures in queue.test.ts left
broken by an unrelated already-merged PR (#2675, security-focused
review profile) that added a required securityFocus field without
updating these fixtures -- main's typecheck was red without this,
which this PR's own CI would otherwise have inherited.
JSONbored added a commit that referenced this pull request Jul 3, 2026
…ion (#2687)

* fix(agent-actions): scope the global open-item cap query by installation

countOpenItemsForAuthorAcrossRepos (merged via #2678, closing #2562)
counted an author's open PRs/issues across the ENTIRE D1 database with
no installation scoping at all -- on a database shared by multiple
installations (the hosted product's normal shape, and possible on
self-host too), a contributor's activity on one installation could
wrongly trigger GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP closes on a completely
unrelated installation that never gated them.

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.

Also fixes 5 AiReviewCacheInput test fixtures in queue.test.ts left
broken by an unrelated already-merged PR (#2675, security-focused
review profile) that added a required securityFocus field without
updating these fixtures -- main's typecheck was red without this,
which this PR's own CI would otherwise have inherited.

* fix(agent-actions): audit a truncated installation repo list, don't drop it silently

listRepoFullNamesForInstallation's .limit(20_000) meant an installation
with more tracked repos than that would silently undercount toward
GLOBAL_CONTRIBUTOR_OPEN_ITEM_CAP with no signal anything was dropped.
Records an audit event on the rare install where the limit is still
hit, mirroring the same observability pattern already used for the
per-author item-count truncation in this file.

Addresses a gate review finding on #2687.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(agent-actions): add an install-wide contributor open-item cap across repos

1 participant