Skip to content

fix(db): order pull-request list queries deterministically - #6388

Merged
JSONbored merged 2 commits into
mainfrom
fix/pull-request-list-ordering
Jul 16, 2026
Merged

fix(db): order pull-request list queries deterministically#6388
JSONbored merged 2 commits into
mainfrom
fix/pull-request-list-ordering

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • `listPullRequests`/`listAllPullRequests` (`src/db/repositories.ts`) both cap their row count (500/2000) with no `ORDER BY` -- Postgres gives no ordering guarantee without one, so each returns an arbitrary, unstable slice of the real population.
  • This is the real root cause behind Sentry's GITTENSORY-1D (10-day recurring "slop score NOT discriminating" ops_anomaly on JSONbored/metagraphed, 96 occurrences): confirmed live that the unordered 500-row cap on a 2930-row repo produced a sample where the `elevated` slop band merged MORE often than `low` (0.450 vs 0.403) -- but over the true full population the score discriminates perfectly (`clean`=0.535 > `low`=0.465 > `elevated`=0.423, monotonically decreasing as intended). The scoring rubric was never broken; the calibration measurement was reading a skewed sample.
  • Fixes it by ordering by descending PR number (single-repo) / descending `createdAt` (cross-repo, since PR numbers reset per repo). Every other caller of these functions (MCP tools, gate-precision, quality metrics, recap) benefits identically -- recent PRs are the relevant population for nearly all of them, an arbitrary historical slice never was.

Test plan

  • New tests confirming both functions return deterministic, recency-ordered results regardless of insert order
  • Full test/unit/db-parsers.test.ts (63 tests), plus the four heaviest consumer test files (backfill, backfill-2, contributor-open-pr-monitor, operator-dashboard; 286 tests) all pass unmodified
  • Full npm run test:ci gate green

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.60%. Comparing base (b429292) to head (198c354).
⚠️ Report is 35 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6388   +/-   ##
=======================================
  Coverage   95.60%   95.60%           
=======================================
  Files         600      600           
  Lines       47245    47246    +1     
  Branches    15035    15036    +1     
=======================================
+ Hits        45167    45168    +1     
  Misses       1291     1291           
  Partials      787      787           
Flag Coverage Δ
shard-1 43.98% <100.00%> (-0.08%) ⬇️
shard-2 36.78% <100.00%> (+0.18%) ⬆️
shard-3 32.29% <100.00%> (-0.23%) ⬇️
shard-4 34.69% <100.00%> (-0.17%) ⬇️
shard-5 31.44% <100.00%> (-0.15%) ⬇️
shard-6 45.35% <100.00%> (+0.44%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/db/repositories.ts 96.80% <100.00%> (+<0.01%) ⬆️

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 16, 2026
@loopover-orb

loopover-orb Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Caution

🛑 LoopOver review result - fixes required

Review updated: 2026-07-16 06:45:37 UTC

2 files · 1 AI reviewer · 2 blockers · CI failing · blocked

🛑 Suggested Action - Manual Review

Review summary
This is a narrow, well-targeted fix: both listPullRequests and listAllPullRequests previously used LIMIT with no ORDER BY, which Postgres/SQLite does not guarantee to be stable or recency-biased, so the 500/2000 row caps returned arbitrary slices. The fix adds orderBy(desc(pullRequests.number)) for the single-repo query and orderBy(desc(pullRequests.createdAt)) for the cross-repo query (correct choice since PR numbers reset per repo but createdAt is a globally comparable ISO string), and both callers ship deterministic tests exercising insert-order-independent output. The root-cause tracing in the description (unordered sample inverting a slop-band calibration check) matches the actual code change — this is a source-layer fix, not a symptom patch.

Nits — 6 non-blocking
  • The PR description doesn't reference an issue number this closes — per repo convention every external contributor PR should link an eligible open issue.
  • src/db/repositories.ts: the 500/2000 row caps remain unexplained magic numbers; consider naming them as constants now that the code is being touched.
  • Worth confirming createdAt has an index if listAllPullRequests's ORDER BY over the full pullRequests table could become a sort bottleneck at scale, though at 2000-row cap this is unlikely to matter today.
  • Consider extracting PULL_REQUEST_LIST_LIMIT / PULL_REQUEST_LIST_LIMIT_ALL constants near the two functions in src/db/repositories.ts for readability.
  • If createdAt is not already indexed, a follow-up migration adding an index would keep listAllPullRequests's ORDER BY cheap as row counts grow.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.

Why this is blocked

  • No linked issue detected — If this PR is intended to solve an issue, link it explicitly in the PR body.
  • Maintainer requires a linked issue — Link the relevant issue (for example Closes #123) before opening the PR.
📋 Copy for AI agents — paste into your coding agent
Fix the following blocker(s) from this PR review:

1. No linked issue detected — If this PR is intended to solve an issue, link it explicitly in the PR body.

2. Maintainer requires a linked issue — Link the relevant issue (for example `Closes #123`) before opening the PR.

CI checks failing

  • validate
  • validate-tests (3)

Decision drivers

  • ❌ Code review — 2 blockers (1 reviewer)
  • ❌ Gate result — Blocking (Repo-configured hard blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (no linked issue context).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 42 registered-repo PR(s), 34 merged, 374 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 42 PR(s), 374 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Review context
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Then work through the remaining 6 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 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 LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 16, 2026
listPullRequests and listAllPullRequests both cap their row count
(500/2000) with no ORDER BY -- Postgres gives no ordering guarantee
without one, so each returned an arbitrary, non-representative slice.

Confirmed live: on a 2930-row repo, the unordered 500-row cap produced
a sample where a higher slop-severity band merged MORE often than a
lower one, tripping src/services/outcome-calibration.ts's discrimination
check and firing a false "slop score not discriminating" ops_anomaly.
Over the true full population the score discriminates correctly
(monotonically decreasing merge rate as severity rises) -- the scoring
rubric itself was never the problem.

Orders by descending PR number (listPullRequests, single-repo) and
descending createdAt (listAllPullRequests, cross-repo -- PR numbers
reset per repo so createdAt is the only globally comparable field).
Every other caller (MCP tools, gate-precision, quality metrics, recap)
benefits the same way: recent PRs are the relevant population for
almost all of them, an arbitrary old slice never was.
@JSONbored
JSONbored force-pushed the fix/pull-request-list-ordering branch from 7dd92a5 to 1809b82 Compare July 16, 2026 06:14
…eatedAt

upsertPullRequestFromGitHub never wrote pr.created_at into the createdAt
column on insert, so it always fell back to the schema's
$defaultFn(() => nowIso()) -- the column silently recorded "when loopover's
own webhook/sync pipeline first saw this row" instead of GitHub's actual PR
creation time, even though PullRequestRecord.createdAt is explicitly
documented as GitHub's own creation time (src/types.ts) and used for
#dup-winner duplicate-cluster election.

This is what broke listAllPullRequests' new ORDER BY createdAt DESC: the
column never reflected real creation order, only insert order, so its own
regression test failed on any set of PRs synced out of chronological order.

Sets createdAt: pr.created_at ?? undefined ONLY in the initial .values()
insert -- deliberately absent from onConflictDoUpdate's set block, since a
PR's real creation date must never change on resync.
@JSONbored
JSONbored merged commit 210b14f into main Jul 16, 2026
13 checks passed
@JSONbored
JSONbored deleted the fix/pull-request-list-ordering branch July 16, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant