Skip to content

fix(queue): guard computeDaysSince so a malformed timestamp can't make the ranking non-deterministic#1543

Open
RenzoMXD wants to merge 2 commits into
JSONbored:mainfrom
RenzoMXD:fix/queue-deterministic-timestamp
Open

fix(queue): guard computeDaysSince so a malformed timestamp can't make the ranking non-deterministic#1543
RenzoMXD wants to merge 2 commits into
JSONbored:mainfrom
RenzoMXD:fix/queue-deterministic-timestamp

Conversation

@RenzoMXD

Copy link
Copy Markdown

Summary

computeDaysSince in src/queue-intelligence.ts parsed a PR timestamp with new Date(isoDateString).getTime() and divided by the day constant with no guard against a non-finite result. A malformed or empty createdAt / lastUpdatedAt yields NaN, which flows into computePrivateBurdenReductionScore (daysSinceCreated * 2 + ...) and then into the analyzePRQueue ranking comparator b.privateBurdenReductionScore - a.privateBurdenReductionScore. An Array.sort comparator that returns NaN is implementation-defined, so the ranked PR list for a repo reorders run-to-run whenever one PR in the set carries a bad timestamp.

The two directly-analogous helpers already guard this case -- daysSince (src/scoring/pending-pr-scenarios.ts) and issueAgeDays (src/signals/reward-risk.ts). computeDaysSince was the lone sibling without the guard.

Fix

Mirror the issueAgeDays guard: when Date.parse is not finite, return 0 (treated as just-created, lowest burden-reduction priority) instead of NaN. The fallback is finite rather than Infinity because Infinity - Infinity is still NaN when two PRs share a bad timestamp -- the sort comparator must stay deterministic for any input, so a finite fallback is required. 0 is also conservative on the recommendation path: a bad lastUpdatedAt is not flagged as pending-too-long. No behavior change for any well-formed input.

Reachability note: live GitHub webhooks carry valid ISO timestamps, but a backfilled / partially-migrated / manually-inserted pull_requests row can carry a malformed or empty value -- the same dirty-data state the rest of the codebase defends against.

Scope

Validation

  • git diff --check
  • npm run typecheck
  • npm run test:coverage -- test/unit/queue-intelligence.test.ts 33/33 pass; scoped coverage on src/queue-intelligence.ts is 100% statements / branches / functions / lines.
  • New behavior has regression tests: a malformed createdAt keeps the ranking deterministic; an empty createdAt degrades to a finite age; a malformed lastUpdatedAt does not trigger the pending-too-long branch.

Targeted run:

npx vitest run test/unit/queue-intelligence.test.ts --coverage --coverage.include='src/queue-intelligence.ts'
# 33/33 passed; src/queue-intelligence.ts 100% statements/branches/functions/lines

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.
  • No auth, cookie, CORS, GitHub App, Cloudflare, or session changes -- pure-function guard, no I/O.
  • No UI changes.
  • No docs/changelog changes.

UI Evidence

Not applicable -- backend pure-function guard with no visible UI, frontend, docs, or extension surface.

Closes #1541

…e the ranking non-deterministic

computeDaysSince parsed a PR timestamp with new Date(...).getTime() and divided
by the day constant with no guard. A malformed/empty createdAt or lastUpdatedAt
yields NaN, which flows into computePrivateBurdenReductionScore and then the
analyzePRQueue sort comparator (b.score - a.score). An Array.sort comparator
that returns NaN is implementation-defined, so the ranked PR list reordered
run-to-run whenever a PR carried a bad timestamp.

Mirror the issueAgeDays guard in signals/reward-risk.ts: when Date.parse is not
finite, return 0 (treated as just-created, lowest burden priority) instead of
NaN. The fallback is finite rather than Infinity because Infinity - Infinity is
still NaN when two PRs share a bad timestamp, and the sort must stay
deterministic for any input.

Closes JSONbored#1541
@RenzoMXD RenzoMXD requested a review from JSONbored as a code owner June 26, 2026 20:53
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jun 26, 2026
@superagent-security

Copy link
Copy Markdown

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

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.37%. Comparing base (b5d575e) to head (af15abf).
⚠️ Report is 6 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1543   +/-   ##
=======================================
  Coverage   95.37%   95.37%           
=======================================
  Files         199      199           
  Lines       21546    21547    +1     
  Branches     7791     7792    +1     
=======================================
+ Hits        20550    20551    +1     
  Misses        416      416           
  Partials      580      580           
Files with missing lines Coverage Δ
src/queue-intelligence.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 added the gittensor:bug Gittensor-scored bug fix - worth 0.5x multiplier. label Jun 26, 2026
@gittensory-orb

gittensory-orb Bot commented Jun 27, 2026

Copy link
Copy Markdown

Caution

🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥

🛑 Gittensory review — blocked

2 files · 1 AI reviewers · no blockers · readiness 55/100 · CI failing · blocked

🛑 Blocked

Review summary
The change replaces `new Date(isoDateString).getTime()` with `Date.parse` guarded by `Number.isFinite`, returning 0 on parse failure instead of NaN. This is correct: NaN in `computePrivateBurdenReductionScore` flows into the `analyzePRQueue` sort comparator and makes `Array.sort` implementation-defined, so the guard must produce a finite value. Returning 0 rather than Infinity is the right call — `Infinity - Infinity` in the comparator would reintroduce NaN when two PRs share a bad timestamp. The three new tests cover the key cases (unparseable string, empty string, staleness path) and all pass.

CI checks failing

  • validate
  • lint
Signal Result Evidence
Code review ✅ No blockers 1 reviewers, synthesized
Linked issue ✅ Linked #1541
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Review load ❌ 8/20 Readiness component derived from cached public PR metadata and labels; size label size:XS.
Validation evidence ❌ 5/25 Cached preflight status is hold.
Open PR queue ❌ 3/10 21 open PR(s), 9 likely reviewable, 12 unlinked.
Contributor context ✅ Confirmed Gittensor contributor RenzoMXD; Gittensor profile; 28 PR(s), 7 issue(s).
Gate result ✅ Passing No configured blocker found.
Nits — 2 non-blocking
  • The inline comment in `computeDaysSince` runs to 5 lines; the `Infinity - Infinity = NaN` rationale is the genuinely non-obvious part worth preserving, but the `issueAgeDays` cross-reference and 'mirrors' note belong in the commit message rather than inline (`src/queue-intelligence.ts`).
  • A bad-timestamp PR and a PR created at the exact moment of evaluation are indistinguishable at debug/log time since both yield a 0-day age; a named constant (e.g. `const MALFORMED_TIMESTAMP_FALLBACK_DAYS = 0`) would make the sentinel self-documenting and easier to grep for if the fallback path ever needs a different value (`src/queue-intelligence.ts`, `computeDaysSince`).
Review context
Contributor next steps
  • Review top overlaps.
  • Add scope summary.
  • Fix blocker.
  • Expect slower review.
  • Refresh registry data or choose a registered active repo.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Review load = cached public PR metadata such as size labels, changed paths, and preflight status.
  • Open PR queue = repo-wide review pressure; it is not a PR quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
Review details

Generated from public PR metadata and the diff. Advisory only; deterministic signals remain authoritative.

The change replaces `new Date(isoDateString).getTime()` with `Date.parse` guarded by `Number.isFinite`, returning 0 on parse failure instead of NaN. This is correct: NaN in `computePrivateBurdenReductionScore` flows into the `analyzePRQueue` sort comparator and makes `Array.sort` implementation-defined, so the guard must produce a finite value. Returning 0 rather than Infinity is the right call — `Infinity - Infinity` in the comparator would reintroduce NaN when two PRs share a bad timestamp. The three new tests cover the key cases (unparseable string, empty string, staleness path) and all pass.

Nits (2)

  • The inline comment in `computeDaysSince` runs to 5 lines; the `Infinity - Infinity = NaN` rationale is the genuinely non-obvious part worth preserving, but the `issueAgeDays` cross-reference and 'mirrors' note belong in the commit message rather than inline (`src/queue-intelligence.ts`).
  • A bad-timestamp PR and a PR created at the exact moment of evaluation are indistinguishable at debug/log time since both yield a 0-day age; a named constant (e.g. `const MALFORMED_TIMESTAMP_FALLBACK_DAYS = 0`) would make the sentinel self-documenting and easier to grep for if the fallback path ever needs a different value (`src/queue-intelligence.ts`, `computeDaysSince`).

🟩 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.

  • Re-run Gittensory review

@gittensory-orb gittensory-orb Bot added the gittensor Gittensor contributor context label Jun 27, 2026
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 - worth 0.5x multiplier. gittensor Gittensor contributor context size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(queue): guard computeDaysSince so a malformed PR timestamp can't make the queue ranking non-deterministic

2 participants