feat(regate-sweep): add opt-in oldest-first ordering mode to selectRegateCandidates#3867
Conversation
…gateCandidates (#3815) The scheduled re-gate sweep's default staleness-based ordering (lastRegatedAt ascending) is deliberately non-deterministic from an operator's perspective: after a repo's first sweep pass, PR selection order is driven by "which PR was re-gated longest ago," not creation order, which looked effectively random to an operator watching a backlog-recovery drain. Adds an opt-in `regateSweepOrderMode` per-repo setting ("staleness" default | "oldest-first"), wired end-to-end: DB column + migration, resolver default, `.gittensory.yml` schema entry (settings: block and the friendly-alias parser), and OpenAPI regen. selectRegateCandidates now takes an `orderMode` param; "oldest-first" sorts by createdAt ascending but preserves an equivalent, timing-independent convergence guarantee to the staleness sort by deferring whichever PR(s) hold the current pool's single most-recent lastRegatedAt value (the immediately preceding sweep's dispatch), with a starvation guard for backlogs small enough to fully clear in one sweep. sweepRepoRegate threads the resolved setting through, so the fanned-out per-PR delaySeconds stagger follows the chosen order.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | f9a5c64 | Commit Preview URL Branch Preview URL |
Jul 06 2026, 08:36 PM |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3867 +/- ##
=======================================
Coverage 93.49% 93.49%
=======================================
Files 333 333
Lines 33206 33228 +22
Branches 12144 12159 +15
=======================================
+ Hits 31045 31067 +22
Misses 1530 1530
Partials 631 631
🚀 New features to boost your workflow:
|
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-06 20:46:18 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 6 non-blocking
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.
|
Summary
lastRegatedAtascending) is deliberately non-deterministic from an operator's perspective — after a repo's first sweep pass, PR selection order is driven by "which PR was re-gated longest ago," not creation order. During today's backlog recovery this looked effectively random to the operator, who wants deterministic oldest-first draining going forward.regateSweepOrderModeper-repo setting ("staleness"default |"oldest-first"), wired end-to-end per this repo's own config-as-code convention:repository_settingsDB column + migration (0116_regate_sweep_order_mode.sql), resolver default (getRepositorySettings/upsertRepositorySettings),.gittensory.ymlschema entry (both the genericsettings:override list and the manifest parser, documented inconfig/examples/gittensory.full.yml+.gittensory.yml.example), and the OpenAPI schemas + regen.selectRegateCandidates(src/settings/agent-sweep.ts) takes a neworderModeparam."oldest-first"sorts candidates bycreatedAtascending, but — sincecreatedAtnever changes — it preserves an equivalent, timing-independent convergence guarantee to the staleness sort (full backlog coverage inceil(open/max)sweeps, even under dry-run/pause) by deferring whichever PR(s) hold the current candidate pool's single most-recentlastRegatedAtvalue (i.e. whichever PR(s) the immediately preceding sweep dispatched — every candidate in one dispatch shares the exact same stamp, seemarkPullRequestsRegated). This is a relative comparison within the pool on every call, not a fixed time window, so it holds regardless of how far apart consecutive sweeps actually run. A starvation guard falls back to the full pool when every eligible PR ties on the samelastRegatedAt(a small backlog whose entire open-PR count fits in one sweep).sweepRepoRegate(src/queue/processors.ts) threads the resolved setting through, so the fanned-out per-PR job dispatch (and itsdelaySecondsstagger) follows the chosen order.Heads-up for the maintainer:
config/examples/gittensory.full.ymlis very close to the hard-coded 64KB manifest-size cap (MAX_FOCUS_MANIFEST_BYTES) this same file is parsed against in tests — after this PR's addition it sits at ~65,522 of 65,536 bytes (14 bytes of headroom). This is a pre-existing structural squeeze from organic growth across many PRs, not something this PR fully addresses; any future field added to this reference file will likely need to either trim existing comments elsewhere in the file or raise the cap.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally;codecov/patchrequires ≥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:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateAll run via
npm run test:ci(full local gate, green) plus a standalonenpm audit --audit-level=moderate(0 vulnerabilities). Also rannpm run db:migrations:check,npm run db:schema-drift:check, andnpm run ui:openapi:settings-parity(the new field is fully in sync across the DB, Drizzle schema, and both hand-authored Zod schemas). New test coverage:test/unit/agent-sweep.test.ts(basic oldest-first order, most-recent-dispatch deferral, the starvation guard, repair-priority interaction, and aceil(open/max)-sweeps convergence regression test spaced 5 minutes apart — well past any fixed window, proving the guarantee is timing-independent) andtest/unit/queue.test.ts(an end-to-end regression asserting the fanned-out per-PRdelaySecondssequence is strictly increasing in creation order).Safety
UI Evidencesection below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails.Backend/config-only change: no auth/CORS/session surface, no interactive app UI, default-off behavior (existing repos see zero change until they opt in).
UI Evidence
N/A — no app UI surface changed; the only "docs" touched are the
.gittensory.ymlreference examples.