diff --git a/README.md b/README.md index 966ad1a..891bc01 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ and the live Diff settings preview. inbox filters All, Authored, and Completed PRs using the signed-in provider identity while keeping the latest-100 query shallow. Each PR gets compact, toolbar-centered Summary, Timeline, and Code tabs: read rendered Markdown descriptions, - follow commits/comments/lifecycle events on one chronology rail, compose + follow commits/comments/lifecycle events newest-first on one chronology rail, compose top-level comments from Summary or Timeline with one preserved draft, and inspect lazily loaded code in the Local Changes-style Pierre file tree with aggregate and selected-file addition/deletion totals, diff --git a/ROADMAP.md b/ROADMAP.md index 5db6e38..329b502 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2452,6 +2452,11 @@ file actions, command-palette destinations, plus pointer and keyboard pane resizing are interactive. Desktop and 900/680 px browser passes found no runtime warnings or horizontal overflow. +**DAN-38 newest-first PR timeline shipped (2026-07-30):** The provider-neutral +pull-request chronology now places the latest commit, comment, or lifecycle +event at the top while retaining deterministic tie ordering. Focused frontend +tests cover merged and closed timelines. + --- ## Cross-cutting tracks (run in parallel with all milestones) diff --git a/TASKS.md b/TASKS.md index 8312b7d..6465427 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1667,7 +1667,9 @@ tree: watch the agent work, review fast, accept or reject safely. `buildPullRequestTimeline`, `PullRequestSummary`): only opened PR detail fetches normalized GitHub/Azure commits; Timeline combines commits, flattened comments, and opened/merged/closed lifecycle markers with stable - ordering, while Summary keeps checks collapsible and readiness persistent. + newest-first ordering, while Summary keeps checks collapsible and readiness + persistent (DAN-38: descending timestamp comparator in + `buildPullRequestTimeline`). - ☑ 1.0 review evolution + local action: safe exact-head **Open branch in worktree…** for GitHub and Azure plus expected-head GitHub **Update branch from target** are shipped (`repo_pull_request_prepare_checkout`, diff --git a/ui/src/lib/pullRequests.test.ts b/ui/src/lib/pullRequests.test.ts index 99105d6..d05142e 100644 --- a/ui/src/lib/pullRequests.test.ts +++ b/ui/src/lib/pullRequests.test.ts @@ -258,7 +258,7 @@ describe('pull request timeline', () => { path: null, }; - it('orders lifecycle, commits, and comments oldest-first with stable ties', () => { + it('orders lifecycle, commits, and comments newest-first with stable ties', () => { const events = buildPullRequestTimeline(pullRequest({ state: 'merged', created_at: '2026-07-15T09:00:00Z', @@ -284,13 +284,13 @@ describe('pull request timeline', () => { comments: [comment], })); expect(events.map((event) => event.id)).toEqual([ - 'opened:42', + 'completed:42', + 'comment:comment-1', `commit:${'a'.repeat(40)}`, `commit:${'b'.repeat(40)}`, - 'comment:comment-1', - 'completed:42', + 'opened:42', ]); - expect(events.at(-1)).toMatchObject({ kind: 'completed', state: 'merged' }); + expect(events[0]).toMatchObject({ kind: 'completed', state: 'merged' }); }); it('deduplicates flattened review comments and emits a closed marker', () => { @@ -300,7 +300,7 @@ describe('pull request timeline', () => { comments: [comment, { ...comment }], })); expect(events.filter((event) => event.kind === 'comment')).toHaveLength(1); - expect(events.at(-1)).toMatchObject({ kind: 'completed', state: 'closed' }); + expect(events[0]).toMatchObject({ kind: 'completed', state: 'closed' }); }); }); diff --git a/ui/src/lib/pullRequests.ts b/ui/src/lib/pullRequests.ts index 7f156e5..11cd68e 100644 --- a/ui/src/lib/pullRequests.ts +++ b/ui/src/lib/pullRequests.ts @@ -101,7 +101,7 @@ export function buildPullRequestTimeline(pr: PullRequest): PullRequestTimelineEv return events.sort((left, right) => { const leftAt = Date.parse(left.at); const rightAt = Date.parse(right.at); - const time = (Number.isNaN(leftAt) ? 0 : leftAt) - (Number.isNaN(rightAt) ? 0 : rightAt); + const time = (Number.isNaN(rightAt) ? 0 : rightAt) - (Number.isNaN(leftAt) ? 0 : leftAt); return time || rank[left.kind] - rank[right.kind] || left.id.localeCompare(right.id); }); } diff --git a/website/docs/pull-requests.md b/website/docs/pull-requests.md index e950b28..c905858 100644 --- a/website/docs/pull-requests.md +++ b/website/docs/pull-requests.md @@ -183,7 +183,7 @@ Timeline, so an unsent draft survives tab switches and refreshes. ### Timeline Timeline orders commits, GitHub issue/review-thread comments, Azure DevOps -thread comments, and opened/merged/closed lifecycle markers oldest-first on one +thread comments, and opened/merged/closed lifecycle markers newest-first on one chronology rail. Commit events show the author, subject, short hash, timestamp, and a provider link when available. Comments render as safe Markdown with author markers, timestamps, and inline file paths. Commit metadata is fetched only for