fix(issues): enforce recent-first sorting across Linear, GitHub, and Jira#1290
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR extracts a shared Key changes:
Minor observations:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| src/main/utils/issueSorting.ts | New shared sorting utility; clean implementation with correct null/invalid handling and non-mutating sort via spread. |
| src/main/utils/tests/issueSorting.test.ts | Good unit coverage for the utility; one test assertion for tied-timestamp ordering implicitly relies on stable sort without documentation. |
| src/main/services/LinearService.ts | Sorts are correctly applied after the GraphQL response is received in both initialFetch and searchIssues. |
| src/main/services/GitHubService.ts | Sort applied after JSON.parse on CLI output for both listIssues and searchIssues; updatedAt is already included in the --json field list. |
| src/main/services/JiraService.ts | Sort consistently applied after every normalizeIssues call; normalizeIssues maps fields.updated → updatedAt which is compatible with HasUpdatedAt. |
| src/test/main/JiraService.test.ts | New tests for initialFetch and smartSearchIssues sorting; searchIssues path is not covered despite also receiving the sort change. |
| src/test/main/LinearService.test.ts | Covers both initialFetch and searchIssues sorting, including the null/invalid timestamp edge cases. |
| src/test/main/GitHubService.test.ts | Adds mock routing for gh issue list commands and two new integration-style tests verifying descending sort for list and search flows. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
GH_LIST["GitHubService.listIssues()"] --> GH_PARSE["JSON.parse(gh issue list output)"]
GH_SEARCH["GitHubService.searchIssues()"] --> GH_PARSE2["JSON.parse(gh issue list --search output)"]
LIN_FETCH["LinearService.initialFetch()"] --> LIN_GQL["GraphQL: issues(orderBy: updatedAt)"]
LIN_SEARCH["LinearService.searchIssues()"] --> LIN_GQL2["GraphQL: searchIssues(term)"]
JIRA_INIT["JiraService.initialFetch()"] --> JIRA_RAW["searchRaw (JQL ORDER BY updated DESC)"]
JIRA_SI["JiraService.searchIssues()"] --> JIRA_RAW2["searchRaw (text ~ term)"]
JIRA_SMART["JiraService.smartSearchIssues()"] --> JIRA_KEY{"Direct key\nlookup?"}
JIRA_KEY -- yes --> JIRA_GET["getIssueByKey()"]
JIRA_KEY -- no --> JIRA_JQL["searchRaw (JQL)"]
GH_PARSE --> SORT["sortByUpdatedAtDesc()"]
GH_PARSE2 --> SORT
LIN_GQL --> SORT
LIN_GQL2 --> SORT
JIRA_RAW --> NORM["normalizeIssues()\n(fields.updated → updatedAt)"]
JIRA_RAW2 --> NORM
JIRA_GET --> NORM
JIRA_JQL --> NORM
NORM --> SORT
SORT --> OUT["Caller receives issues\nsorted most-recent-first"]
subgraph issueSorting.ts
SORT
TS["updatedAtToTimestamp()\nnull / invalid → 0"]
SORT --> TS
end
Last reviewed commit: f0bfd80
| it('pushes missing or invalid updatedAt to the end', () => { | ||
| const issues = [ | ||
| { id: 'missing', updatedAt: null }, | ||
| { id: 'valid', updatedAt: '2026-03-04T00:00:00.000Z' }, | ||
| { id: 'invalid', updatedAt: 'bogus' }, | ||
| ]; | ||
|
|
||
| const sorted = sortByUpdatedAtDesc(issues); | ||
| expect(sorted.map((issue) => issue.id)).toEqual(['valid', 'missing', 'invalid']); | ||
| }); |
There was a problem hiding this comment.
Implicit stable-sort dependency in test assertion
The expected output ['valid', 'missing', 'invalid'] relies on a stable sort to preserve the relative input order of 'missing' (index 0) and 'invalid' (index 2) since both map to timestamp 0. JavaScript's Array.prototype.sort has been required to be stable since ECMAScript 2019, so this will work on all modern runtimes, but the test gives no indication that this assumption is intentional.
Consider either documenting the assumption with a comment, or explicitly testing only that the two invalid entries appear after the valid one (e.g. checking sorted[0].id === 'valid' and that the set of the remaining two items equals new Set(['missing', 'invalid'])). This is especially relevant because the same pattern appears in LinearService.test.ts (lines 56–63), where GEN-200 (null) and GEN-201 ('not-a-date') are also expected to be in a specific relative order despite being tied at timestamp 0.
| import { beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest'; | ||
|
|
||
| vi.mock('electron', () => ({ | ||
| app: { getPath: () => '/tmp/test-emdash' }, | ||
| })); |
There was a problem hiding this comment.
Missing test coverage for searchIssues
The new JiraService.test.ts covers initialFetch and smartSearchIssues, but JiraService.searchIssues (a separate public method that also received the sortByUpdatedAtDesc wrapping in this PR at line 164 of JiraService.ts) has no test. For full coverage of the new sorting behaviour it's worth adding a test for that path as well.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Testing
Context
Note
Low Risk
Low risk behavior change limited to client-side ordering of issue lists/search results across GitHub/Jira/Linear, with coverage for invalid/missing timestamps.
Overview
Enforces deterministic recent-first issue ordering across providers. GitHub (
listIssues/searchIssues), Jira (initialFetch/searchIssues/smartSearchIssues), and Linear (initialFetch/searchIssues) now all sort results byupdatedAtdescending via a sharedsortByUpdatedAtDescutility, pushing missing/invalid timestamps to the end.Adds unit tests for the new sorting utility and provider-specific tests to assert the new ordering behavior.
Written by Cursor Bugbot for commit bbe4026. This will update automatically on new commits. Configure here.