fix: order sessions by creation time#115
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 8 minutes and 31 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (22)
📝 WalkthroughWalkthroughThis pull request modifies session sorting behavior throughout the application. Sessions are now ordered by Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request implements session sorting and pagination by creation time across the frontend and backend. It updates session mapping in the layout and navigation components and adds API support for creation-time cursors. The PR also includes significant structural changes to the generated SDK, such as renaming core classes like Project to Workspace. Feedback was provided regarding the lack of error handling in the cursor decoding logic, which could lead to server-side exceptions if malformed input is provided.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f035f6e722
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/app/src/pages/layout/helpers.ts`:
- Around line 17-23: The sortSessions function no longer uses the now parameter,
so remove the unused argument from its signature and update any callers (notably
sortedRootSessions and latestRootSession) to stop accepting/forwarding now;
change sortSessions to function sortSessions() and adjust sortedRootSessions and
latestRootSession signatures and internal calls to call sortSessions() directly
and stop passing now through the chain, then run TypeScript checks to update any
remaining references to the removed parameter (Session type usages remain
unchanged).
In `@packages/opencode/src/server/instance/experimental.ts`:
- Around line 45-56: Both decoders treat 0 as absent and
decodeCreatedSessionCursor can throw on invalid base64/JSON; change the nil
check from if (!value) to explicit value === undefined || value === null so 0 is
preserved, and wrap the string decode/JSON parse and Zod validation in a
try/catch (or use z.safeParse) inside decodeCreatedSessionCursor to return
undefined on malformed input instead of throwing; in decodeUpdatedSessionCursor
accept numbers (including 0) or convert strings via Number(value) and only
return the numeric cursor if Number.isFinite(cursor), otherwise undefined.
Ensure references to decodeCreatedSessionCursor, decodeUpdatedSessionCursor, and
SessionID.zod are used when locating the code to update.
In `@packages/opencode/test/server/session-list.test.ts`:
- Around line 115-147: Convert the two timing-sensitive tests "keeps default
ordering by last update for existing clients" and "orders root sessions by
creation time when requested" from plain test(...) to the repo's Effect harness
by using testEffect(...) and mark them as live-time with it.live(...);
specifically update the test declarations to testEffect(it.live(...), async ()
=> { ... }) (preserving the existing body that uses tmpdir, Instance.provide,
svc.create, svc.touch and svc.list), and ensure the file imports testEffect and
it from test/lib/effect.ts (replacing the plain test import/usage) so these
tests run under the Effect runtime and are executed as live tests to avoid
flakiness.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: fab4adee-33f9-4e25-af3a-0aa0b64b8403
⛔ Files ignored due to path filters (2)
packages/sdk/js/src/v2/gen/sdk.gen.tsis excluded by!**/gen/**packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (18)
packages/app/src/context/global-sync.test.tspackages/app/src/context/global-sync/session-load.tspackages/app/src/context/global-sync/session-trim.test.tspackages/app/src/context/global-sync/session-trim.tspackages/app/src/context/global-sync/types.tspackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/helpers.test.tspackages/app/src/pages/layout/helpers.tspackages/app/src/pages/layout/pawwork-session-nav.test.tspackages/app/src/pages/layout/pawwork-session-nav.tspackages/app/src/pages/layout/pawwork-session-source.test.tspackages/app/src/pages/layout/pawwork-session-source.tspackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/opencode/src/server/instance/experimental.tspackages/opencode/src/server/instance/session.tspackages/opencode/src/session/session.tspackages/opencode/test/server/global-session-list.test.tspackages/opencode/test/server/session-list.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/app/src/pages/layout.tsx (1)
1417-1432:⚠️ Potential issue | 🟠 MajorPass
sort: "created"in the fallback session fetch.Line 1426 still uses the API default ordering. Because
latestRootSession()now selects bytime.created, this fallback can still open the wrong session when an older thread has recent activity and pushes the newer-created root off the first page.♻️ Proposed fix
const fetched = latestRootSession( await Promise.all( dirs.map(async (item) => ({ path: { directory: item }, session: await globalSDK.client.session - .list({ directory: item }) + .list({ directory: item, sort: "created" }) .then((x) => x.data ?? []) .catch(() => []), })), ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app/src/pages/layout.tsx` around lines 1417 - 1432, The fallback fetch is using the API default ordering so latestRootSession can pick the wrong session; update the Promise.all mapping where globalSDK.client.session.list is called (inside the dirs.map that builds fetched) to pass sort: "created" in the list call options so the list request orders by creation time (i.e., change the call used when constructing fetched to globalSDK.client.session.list({ directory: item, sort: "created" }) so latestRootSession and openSession operate on sessions ordered by time.created).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/opencode/test/server/global-session-list.test.ts`:
- Around line 110-130: Replace the timing-sensitive plain test "keeps default
global ordering by last update for existing clients" with the testEffect +
it.live pattern used elsewhere: change the test(...) declaration to
testEffect(() => it.live(async () => { ... })) (move the async body unchanged
inside the it.live callback), ensure you import/use the same testEffect and it
utilities as used in session-list.test.ts, and apply the same migration to the
sibling tests around lines 86-108 so all timing-dependent uses of
setTimeout/Instance.provide/ svc.create / svc.touch / svc.listGlobal run under
it.live.
---
Outside diff comments:
In `@packages/app/src/pages/layout.tsx`:
- Around line 1417-1432: The fallback fetch is using the API default ordering so
latestRootSession can pick the wrong session; update the Promise.all mapping
where globalSDK.client.session.list is called (inside the dirs.map that builds
fetched) to pass sort: "created" in the list call options so the list request
orders by creation time (i.e., change the call used when constructing fetched to
globalSDK.client.session.list({ directory: item, sort: "created" }) so
latestRootSession and openSession operate on sessions ordered by time.created).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f5e76fc7-3fcd-4adb-bd47-23ddb1a6d3be
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (11)
packages/app/src/context/global-sync.tsxpackages/app/src/context/global-sync/bootstrap.tspackages/app/src/context/global-sync/event-reducer.test.tspackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/helpers.test.tspackages/app/src/pages/layout/helpers.tspackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/opencode/src/server/instance/experimental.tspackages/opencode/test/server/global-session-list.test.tspackages/opencode/test/server/session-list.test.ts
d5a8f9b to
e71e1d7
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Astro-Han
left a comment
There was a problem hiding this comment.
Code Review
P1 - ID tiebreaker direction inconsistency
Multiple sorting functions use different ID ordering directions:
compareSessionRecent:cmp(a.id, b.id)→ ascending- New
sortSessionsin helpers.ts:b.id.localeCompare(a.id)→ descending compareSessionCreated:b.id.localeCompare(a.id)→ descending
Sessions with equal creation timestamps will appear in reverse ID order compared to sessions with equal update timestamps. Consider standardizing to one direction.
Files affected:
packages/app/src/pages/layout/helpers.ts:17packages/app/src/context/global-sync/session-trim.ts:19
P1 - Missing tiebreaker test coverage
The new sorting tests don't cover identical time.created values. The ID tiebreaker b.id.localeCompare(a.id) is never exercised and could regress.
File: packages/app/src/pages/layout/helpers.test.ts:161
P2 - Confusing test name
session-trim.test.ts test "selects base roots by creation time before returning id-sorted store order" - expected result is creation-time sorted, not ID sorted.
File: packages/app/src/context/global-sync/session-trim.test.ts:23
P2 - Cursor edge case
Numeric cursor with sort=created loses ID tiebreaker, potentially returning duplicates when sessions share creation timestamps.
File: packages/opencode/src/server/instance/experimental.ts:77
P2 - skipToken replacement
Replacing skipToken with noopQuery changes query behavior (suspend vs run). Should document safety rationale.
File: packages/app/src/context/global-sync.tsx:50
P2 - Mixed tiebreaker directions
pawwork-session-source.ts has three-level tiebreaker with inconsistent directions: creation (desc), projectLabel (asc), id (desc).
File: packages/app/src/pages/layout/pawwork-session-source.ts:42
60f3323 to
b5d1a52
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Review Summary
This PR correctly implements creation-time ordering for sessions. The core logic is sound, and tests cover the main functionality well. However, I found several issues that should be addressed before merging:
P0 Issues (blocking)
-
Unrelated refactoring mixed in (packages/app/src/context/global-sync.tsx, bootstrap.ts)
- Removing import and replacing with plain objects is an unrelated refactor. This should be in a separate commit with its own justification. provides better TypeScript inference for TanStack Query.
-
Tiebreaker behavior change without documentation
- The ID tiebreaker changed from ascending () to descending (). This affects session ordering when sessions have identical timestamps. This behavior change is not documented in the PR description.
P1 Issues (should fix)
-
Missing tiebreaker tests (helpers.test.ts, pawwork-session-source.test.ts)
- No tests verify sorting behavior when timestamps are identical. The tiebreaker logic should be explicitly tested.
-
New function without direct tests (session-trim.ts)
- is added but only indirectly tested through . A direct unit test would make the sorting contract explicit.
-
Legacy cursor edge case (session.ts)
- When a legacy numeric cursor is passed to , sessions with identical timestamps but different IDs may be skipped. Consider documenting or handling this edge case.
Suggestions
- Add a test case for identical timestamps to document tiebreaker behavior
- Split the refactor into a separate commit
- Document the tiebreaker direction change in the PR description if intentional
Astro-Han
left a comment
There was a problem hiding this comment.
Review Summary
This PR correctly implements creation-time ordering for sessions. The core logic is sound, and tests cover the main functionality well. However, I found several issues that should be addressed before merging:
P0 Issues (blocking)
-
Unrelated refactoring mixed in (packages/app/src/context/global-sync.tsx, bootstrap.ts)
- Removing
queryOptionsimport and replacing with plain objects is an unrelated refactor. This should be in a separate commit with its own justification.queryOptionsprovides better TypeScript inference for TanStack Query.
- Removing
-
Tiebreaker behavior change without documentation
- The ID tiebreaker changed from ascending (
a.id.localeCompare(b.id)) to descending (b.id.localeCompare(a.id)). This affects session ordering when sessions have identical timestamps. This behavior change is not documented in the PR description.
- The ID tiebreaker changed from ascending (
P1 Issues (should fix)
-
Missing tiebreaker tests (helpers.test.ts, pawwork-session-source.test.ts)
- No tests verify sorting behavior when
createdtimestamps are identical. The tiebreaker logic should be explicitly tested.
- No tests verify sorting behavior when
-
New function without direct tests (session-trim.ts)
compareSessionCreatedis added but only indirectly tested throughtrimSessions. A direct unit test would make the sorting contract explicit.
-
Legacy cursor edge case (session.ts)
- When a legacy numeric cursor is passed to
sort=created, sessions with identical timestamps but different IDs may be skipped. Consider documenting or handling this edge case.
- When a legacy numeric cursor is passed to
Suggestions
- Add a test case for identical
createdtimestamps to document tiebreaker behavior - Split the
queryOptionsrefactor into a separate commit - Document the tiebreaker direction change in the PR description if intentional
948f447 to
e302828
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/opencode/src/server/instance/experimental.ts`:
- Around line 374-381: The cursor schema currently uses z.coerce.number() which
turns an empty query string ("?cursor=") into 0 and causes
decodeUpdatedSessionCursor() to treat it as a valid timestamp; update the cursor
Zod schema (the cursor field in the sessions query schema) to preprocess empty
strings into undefined (using z.preprocess) before applying
z.coerce.number().or(z.string()).optional() so that empty cursor parameters
become undefined and pagination falls back to the first page; ensure the change
preserves existing handling of numeric and string cursors and still forwards
undefined to decodeUpdatedSessionCursor() when no cursor was provided.
In `@packages/opencode/src/session/session.ts`:
- Around line 776-780: The sort-to-order mapping used in list(...) and
listGlobal(...) is duplicated (the logic that sets sort, order based on
"created" vs default "updated"); extract this into a shared helper function
(e.g., getSessionOrder(sort: string) or computeSessionOrder) that returns the
array of ordering expressions, and replace the inline logic in list and
listGlobal with a call to that helper; ensure the helper references
SessionTable.time_created, SessionTable.time_updated and SessionTable.id so both
callers use the exact same ordering definition and future changes are
centralized.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0defae56-7307-4ec9-8e58-895a4688eaab
📒 Files selected for processing (19)
packages/app/src/context/global-sync.test.tspackages/app/src/context/global-sync/event-reducer.test.tspackages/app/src/context/global-sync/session-load.tspackages/app/src/context/global-sync/session-trim.test.tspackages/app/src/context/global-sync/session-trim.tspackages/app/src/context/global-sync/types.tspackages/app/src/pages/layout.tsxpackages/app/src/pages/layout/helpers.test.tspackages/app/src/pages/layout/helpers.tspackages/app/src/pages/layout/pawwork-session-nav.test.tspackages/app/src/pages/layout/pawwork-session-nav.tspackages/app/src/pages/layout/pawwork-session-source.test.tspackages/app/src/pages/layout/pawwork-session-source.tspackages/app/src/pages/layout/pawwork-sidebar.tsxpackages/app/src/pages/layout/sidebar-project.tsxpackages/app/src/pages/layout/sidebar-workspace.tsxpackages/opencode/src/server/instance/experimental.tspackages/opencode/src/server/instance/session.tspackages/opencode/src/session/session.ts
e302828 to
e0a4ded
Compare
Astro-Han
left a comment
There was a problem hiding this comment.
Review Summary (Round 2)
Previous issues (P0: queryOptions refactor, tiebreaker direction) are now fixed. Tests for tiebreaker scenarios have been added. The implementation is solid.
P2 Issues (nitpicks, non-blocking)
-
Database vs frontend ID ordering inconsistency (session.ts:900-904)
- Database uses
desc(SessionTable.id)for tiebreaker (ID descending: z before a) - Frontend uses
a.id.localeCompare(b.id)for tiebreaker (ID ascending: a before z) - Direct API consumers will see different ordering than frontend UI users
- Suggestion: Align to
asc(SessionTable.id)or document this difference
- Database uses
-
cmpvslocaleCompareinconsistency (session-trim.ts)compareSessionCreatedusescmp(a.id, b.id)- Other sorting functions use
a.id.localeCompare(b.id) - Both work for ASCII IDs, but stylistic inconsistency
- Suggestion: Use one consistently or add comment explaining choice
-
Test doesn't document API tiebreaker behavior (global-session-list.test.ts)
- Test creates sessions with identical
createdtime but doesn't verify which ID comes first - Adding explicit ID ordering assertion would document API behavior
- Test creates sessions with identical
-
Zod preprocess chain fragility (experimental.ts)
z.preprocesswithz.coerce.number().or(z.string()).optional()works but type inference may be unclear- Consider using
z.union([...]).optional()for clarity
Verdict
All blocking issues resolved. The above are minor improvements that can be addressed in a follow-up or accepted as-is since they don't affect correctness. Ready to merge after addressing P2 items if desired.
e0a4ded to
e218df9
Compare
Code Review SummaryOverall this is a well-structured change that correctly implements creation-time ordering for sessions. The backend API changes preserve backward compatibility by keeping Below are some nit-level observations for potential improvements: 1. Code duplication:
|
e218df9 to
426c149
Compare
Summary
Sessions in the PawWork desktop sidebar now keep creation-time order instead of jumping when an older session receives activity. Backend session APIs keep updated-time ordering by default and add an explicit
sort=createdoption for the desktop root-session load path.Why
The previous UI behavior used last updated time in several places, so any action in an older session moved it above newer sessions. Creation-time order is more stable and matches the expected session list mental model.
Related Issue
No linked issue. This PR fixes a bug reported directly in the coding session.
How To Verify
Screenshots or Recordings
Not included. This changes ordering behavior only; targeted tests cover the visible session-list ordering logic.
Checklist
dev, and my PR title and commit messages use Conventional Commits in EnglishSummary by CodeRabbit
New Features
sortquery parameter on session list endpointsRefactor