Skip to content

fix: order sessions by creation time#115

Merged
Astro-Han merged 3 commits into
devfrom
codex/fix-session-order
Apr 22, 2026
Merged

fix: order sessions by creation time#115
Astro-Han merged 3 commits into
devfrom
codex/fix-session-order

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Apr 21, 2026

Copy link
Copy Markdown
Owner

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=created option 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

cd packages/opencode
bun test --timeout 30000 test/server/session-list.test.ts test/server/global-session-list.test.ts
bun run typecheck
cd ../app
bun test --preload ./happydom.ts ./src/context/global-sync.test.ts ./src/context/global-sync/session-trim.test.ts ./src/pages/layout/helpers.test.ts ./src/pages/layout/pawwork-session-source.test.ts ./src/pages/layout/pawwork-session-nav.test.ts
bun run typecheck

Screenshots or Recordings

Not included. This changes ordering behavior only; targeted tests cover the visible session-list ordering logic.

Checklist

  • I linked the related issue, or stated why there is no issue
  • This PR has type, scope, and priority labels, or I requested maintainer labeling
  • I listed the relevant verification steps, including tests when behavior changed
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for desktop, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, or generated/local file changes when relevant
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • New Features

    • Sessions can now be sorted by creation time via the sort query parameter on session list endpoints
    • Added cursor-based pagination support for creation-time sorted sessions
  • Refactor

    • Removed time-windowing session sort logic in favor of strict creation-time ordering
    • Implemented deterministic tie-breaking using session IDs for consistent, reproducible results

@Astro-Han Astro-Han added bug Something isn't working P2 Medium priority app Application behavior and product flows labels Apr 21, 2026
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 31 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db8bc17e-878b-481d-9428-1a87d529f752

📥 Commits

Reviewing files that changed from the base of the PR and between e302828 and 426c149.

⛔ Files ignored due to path filters (2)
  • packages/sdk/js/src/v2/gen/sdk.gen.ts is excluded by !**/gen/**
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (22)
  • packages/app/src/context/global-sync.test.ts
  • packages/app/src/context/global-sync/event-reducer.test.ts
  • packages/app/src/context/global-sync/session-load.ts
  • packages/app/src/context/global-sync/session-trim.test.ts
  • packages/app/src/context/global-sync/session-trim.ts
  • packages/app/src/context/global-sync/types.ts
  • packages/app/src/context/global-sync/utils.ts
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/helpers.test.ts
  • packages/app/src/pages/layout/helpers.ts
  • packages/app/src/pages/layout/pawwork-session-nav.test.ts
  • packages/app/src/pages/layout/pawwork-session-nav.ts
  • packages/app/src/pages/layout/pawwork-session-source.test.ts
  • packages/app/src/pages/layout/pawwork-session-source.ts
  • packages/app/src/pages/layout/pawwork-sidebar.tsx
  • packages/app/src/pages/layout/sidebar-project.tsx
  • packages/app/src/pages/layout/sidebar-workspace.tsx
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/session.ts
  • packages/opencode/test/server/global-session-list.test.ts
  • packages/opencode/test/server/session-list.test.ts
📝 Walkthrough

Walkthrough

This pull request modifies session sorting behavior throughout the application. Sessions are now ordered by created timestamp instead of updated, with deterministic tie-breaking by id. Changes include backend sorting parameter support, cursor pagination updates for created-time pagination, frontend helper function updates, component prop removal, and corresponding test coverage.

Changes

Cohort / File(s) Summary
Backend Session Listing
packages/opencode/src/session/session.ts, packages/opencode/src/server/instance/session.ts
Added sort parameter ("updated" | "created") to list() and listGlobal() methods; updated SQL ordering to use time_created or time_updated based on sort mode; changed listGlobal() cursor type from number to number | { created, id } for created-time pagination.
Backend Experimental Route
packages/opencode/src/server/instance/experimental.ts
Implemented cursor encoding/decoding helpers for created-time pagination; extended /experimental/session query schema to accept sort enum and cursor as number | string; updated response header logic to emit base64url-encoded cursor for sort=created or numeric cursor for sort=updated.
Global Sync Core
packages/app/src/context/global-sync/session-load.ts, packages/app/src/context/global-sync/session-trim.ts, packages/app/src/context/global-sync/types.ts
Updated RootLoadArgs type to include optional sort?: "created" parameter; modified loadRootSessionsWithFallback() to pass sort: "created" to input.list(); introduced compareSessionCreated() comparator in trimSessions() to sort base roots by time.created before selecting limit.
Frontend Sorting Helpers
packages/app/src/pages/layout/helpers.ts
Replaced time-windowed sortSessions() comparator with simple descending created order plus id tie-breaker; removed now parameter from sortedRootSessions() and latestRootSession() function signatures; updated both helpers to always use new sorting logic.
Layout Component
packages/app/src/pages/layout/layout.tsx
Removed sortNow state field and associated timer/interval logic; updated session sorting calls to remove Date.now() arguments; added sort: "created" to global SDK session list request; removed sortNow prop from workspace sidebar components.
Workspace Sidebar Components
packages/app/src/pages/layout/sidebar-workspace.tsx, packages/app/src/pages/layout/sidebar-project.tsx
Removed sortNow: Accessor<number> prop from SortableWorkspace, LocalWorkspace, and SortableProject; updated session sorting invocations to call sortedRootSessions() without second argument.
Pawwork Session Navigation
packages/app/src/pages/layout/pawwork-session-nav.ts, packages/app/src/pages/layout/pawwork-session-nav.test.ts
Updated PawworkSessionItem type: changed updated: number to created: number; modified buildPawworkSessionSections() to sort by descending created (not updated) with id ascending tie-breaker in both "time" and "project" modes.
Pawwork Session Sorting
packages/app/src/pages/layout/pawwork-session-source.ts, packages/app/src/pages/layout/pawwork-session-source.test.ts
Changed SessionLike type field from updated: number to created: number; updated sortPawworkSidebarSessions() comparator to sort by descending created instead of updated, preserving secondary projectLabel and id ordering.
Pawwork Sidebar Model
packages/app/src/pages/layout/pawwork-sidebar.tsx
Changed PawworkSidebarSession exported type: renamed field from updated: number to created: number; updated mapping to pass created: item.created instead of updated: item.updated.
Test Updates: Global Sync
packages/app/src/context/global-sync/session-load.test.ts, packages/app/src/context/global-sync/session-trim.test.ts, packages/app/src/context/global-sync/event-reducer.test.ts
Extended mocked calls query shape to include sort?: "created"; added new trimSessions() tests verifying selection by created time with id tie-breaking; updated rootSession() helper to accept optional created and updated parameters for deterministic test data.
Test Updates: Frontend Helpers
packages/app/src/pages/layout/helpers.test.ts
Removed second Date.now() argument from all latestRootSession() calls; added test coverage for sortedRootSessions() sorting by descending created with id tie-breaker; added coverage for latestRootSession() selecting latest visible root by created.
Test Updates: Backend Session List
packages/opencode/test/server/session-list.test.ts, packages/opencode/test/server/global-session-list.test.ts
Added touch(sessionID) helper for updating session modification time; added integration tests verifying default sort by updated, sort: "created" ordering, and created-time cursor pagination round-tripping; added route-level tests for /session and /experimental/session with sort parameter and cursor handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

P2

Poem

🐰 The sessions now hop by creation's light,
No more by updates—created sets the flight!
With id as tiebreaker, ordered just right,
Pagination hops forward through cursor's delight. 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: order sessions by creation time' directly and concisely summarizes the main change—switching session ordering from last-updated to creation-time basis.
Description check ✅ Passed The PR description includes all required sections (Summary, Why, Related Issue, How To Verify, Checklist) with comprehensive details; all checklist items are marked as completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-session-order

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/opencode/src/server/instance/experimental.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/opencode/src/server/instance/experimental.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 63a8a52 and f035f6e.

⛔ Files ignored due to path filters (2)
  • packages/sdk/js/src/v2/gen/sdk.gen.ts is excluded by !**/gen/**
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (18)
  • packages/app/src/context/global-sync.test.ts
  • packages/app/src/context/global-sync/session-load.ts
  • packages/app/src/context/global-sync/session-trim.test.ts
  • packages/app/src/context/global-sync/session-trim.ts
  • packages/app/src/context/global-sync/types.ts
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/helpers.test.ts
  • packages/app/src/pages/layout/helpers.ts
  • packages/app/src/pages/layout/pawwork-session-nav.test.ts
  • packages/app/src/pages/layout/pawwork-session-nav.ts
  • packages/app/src/pages/layout/pawwork-session-source.test.ts
  • packages/app/src/pages/layout/pawwork-session-source.ts
  • packages/app/src/pages/layout/pawwork-sidebar.tsx
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/session.ts
  • packages/opencode/test/server/global-session-list.test.ts
  • packages/opencode/test/server/session-list.test.ts

Comment thread packages/app/src/pages/layout/helpers.ts Outdated
Comment thread packages/opencode/src/server/instance/experimental.ts
Comment thread packages/opencode/test/server/session-list.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Pass sort: "created" in the fallback session fetch.

Line 1426 still uses the API default ordering. Because latestRootSession() now selects by time.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

📥 Commits

Reviewing files that changed from the base of the PR and between f035f6e and 677b35a.

⛔ Files ignored due to path filters (1)
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (11)
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/context/global-sync/bootstrap.ts
  • packages/app/src/context/global-sync/event-reducer.test.ts
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/helpers.test.ts
  • packages/app/src/pages/layout/helpers.ts
  • packages/app/src/pages/layout/sidebar-project.tsx
  • packages/app/src/pages/layout/sidebar-workspace.tsx
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/test/server/global-session-list.test.ts
  • packages/opencode/test/server/session-list.test.ts

Comment thread packages/opencode/test/server/global-session-list.test.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch from d5a8f9b to e71e1d7 Compare April 22, 2026 07:32
@Astro-Han

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

P1 - ID tiebreaker direction inconsistency

Multiple sorting functions use different ID ordering directions:

  • compareSessionRecent: cmp(a.id, b.id) → ascending
  • New sortSessions in 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:17
  • packages/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

@Astro-Han Astro-Han added P3 Low priority and removed P2 Medium priority labels Apr 22, 2026
@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch 2 times, most recently from 60f3323 to b5d1a52 Compare April 22, 2026 10:26
Repository owner deleted a comment from coderabbitai Bot Apr 22, 2026
Comment thread packages/app/src/context/global-sync.tsx
Comment thread packages/app/src/context/global-sync/bootstrap.ts Outdated
Comment thread packages/app/src/pages/layout/helpers.ts
Comment thread packages/app/src/pages/layout/pawwork-session-nav.ts
Comment thread packages/app/src/pages/layout/pawwork-session-source.ts
Comment thread packages/app/src/pages/layout/helpers.test.ts
Comment thread packages/app/src/context/global-sync/session-trim.ts Outdated
Comment thread packages/opencode/src/session/session.ts Outdated

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

  1. 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.
  2. 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)

  1. 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.
  2. 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.
  3. 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 Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

  1. Unrelated refactoring mixed in (packages/app/src/context/global-sync.tsx, bootstrap.ts)

    • Removing queryOptions import and replacing with plain objects is an unrelated refactor. This should be in a separate commit with its own justification. queryOptions provides better TypeScript inference for TanStack Query.
  2. 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.

P1 Issues (should fix)

  1. Missing tiebreaker tests (helpers.test.ts, pawwork-session-source.test.ts)

    • No tests verify sorting behavior when created timestamps are identical. The tiebreaker logic should be explicitly tested.
  2. New function without direct tests (session-trim.ts)

    • compareSessionCreated is added but only indirectly tested through trimSessions. A direct unit test would make the sorting contract explicit.
  3. 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.

Suggestions

  • Add a test case for identical created timestamps to document tiebreaker behavior
  • Split the queryOptions refactor into a separate commit
  • Document the tiebreaker direction change in the PR description if intentional

@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch 3 times, most recently from 948f447 to e302828 Compare April 22, 2026 11:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 677b35a and e302828.

📒 Files selected for processing (19)
  • packages/app/src/context/global-sync.test.ts
  • packages/app/src/context/global-sync/event-reducer.test.ts
  • packages/app/src/context/global-sync/session-load.ts
  • packages/app/src/context/global-sync/session-trim.test.ts
  • packages/app/src/context/global-sync/session-trim.ts
  • packages/app/src/context/global-sync/types.ts
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/helpers.test.ts
  • packages/app/src/pages/layout/helpers.ts
  • packages/app/src/pages/layout/pawwork-session-nav.test.ts
  • packages/app/src/pages/layout/pawwork-session-nav.ts
  • packages/app/src/pages/layout/pawwork-session-source.test.ts
  • packages/app/src/pages/layout/pawwork-session-source.ts
  • packages/app/src/pages/layout/pawwork-sidebar.tsx
  • packages/app/src/pages/layout/sidebar-project.tsx
  • packages/app/src/pages/layout/sidebar-workspace.tsx
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/src/server/instance/session.ts
  • packages/opencode/src/session/session.ts

Comment thread packages/opencode/src/server/instance/experimental.ts Outdated
Comment thread packages/opencode/src/session/session.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch from e302828 to e0a4ded Compare April 22, 2026 11:24
Comment thread packages/opencode/src/session/session.ts
Comment thread packages/app/src/context/global-sync/session-trim.ts Outdated
Comment thread packages/opencode/test/server/global-session-list.test.ts
Comment thread packages/opencode/src/server/instance/experimental.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

  1. 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
  2. cmp vs localeCompare inconsistency (session-trim.ts)

    • compareSessionCreated uses cmp(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
  3. Test doesn't document API tiebreaker behavior (global-session-list.test.ts)

    • Test creates sessions with identical created time but doesn't verify which ID comes first
    • Adding explicit ID ordering assertion would document API behavior
  4. Zod preprocess chain fragility (experimental.ts)

    • z.preprocess with z.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.

@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch from e0a4ded to e218df9 Compare April 22, 2026 12:22
Comment thread packages/opencode/src/session/session.ts Outdated
Comment thread packages/app/src/pages/layout/helpers.ts
Comment thread packages/opencode/src/session/session.ts
Comment thread packages/app/src/context/global-sync/session-trim.ts Outdated
Comment thread packages/opencode/test/server/global-session-list.test.ts
Comment thread packages/opencode/src/server/instance/experimental.ts
Comment thread packages/app/src/pages/layout/helpers.ts
@Astro-Han

Copy link
Copy Markdown
Owner Author

Code Review Summary

Overall this is a well-structured change that correctly implements creation-time ordering for sessions. The backend API changes preserve backward compatibility by keeping sort=updated as default, and the frontend consistently uses creation-time ordering for session list display.

Below are some nit-level observations for potential improvements:


1. Code duplication: compareSessionCreated vs sortSessions()

File: packages/app/src/context/global-sync/session-trim.ts:13

The compareSessionCreated function has identical logic to the comparator returned by sortSessions() in helpers.ts. This duplication risks inconsistent behavior if one is modified without the other.

Suggestion: Export compareSessionCreated (or a shared sessionCreatedComparator) and reuse it in helpers.ts instead of defining sortSessions() inline.


2. Function naming clarity

File: packages/app/src/pages/layout/helpers.ts:14

sortSessions() returns a comparator function, not a sort operation. The name sortSessions suggests it performs sorting directly. Readers seeing roots(store).sort(sortSessions()) might expect sortSessions to be the sort function itself.

Suggestion: Rename to compareSessionsByCreated or sessionCreatedComparator for clarity.


3. Fallback value for missing time.created

File: packages/app/src/pages/layout.tsx:235

created: session.time?.created ?? 0

Using 0 as the fallback means sessions without time metadata will be treated as created at epoch (the oldest possible). This could lead to unexpected ordering if sessions somehow lack time fields.

Suggestion: Consider using session.time?.updated as a secondary fallback, or at least document this behavior. If missing time is unexpected, a warning log could help catch data integrity issues.


4. Missing comment for graceful cursor degradation

File: packages/opencode/src/session/session.ts:960-961

When sort === 'created' and cursor is a number, no cursor condition is added—the pagination silently returns all matching sessions. This is tested in global-session-list.test.ts but lacks an explanatory comment.

Suggestion: Add a brief comment:

// Numeric cursor is invalid for created-order pagination; gracefully ignore

5. Inconsistent comparator style

File: packages/app/src/pages/layout/pawwork-session-nav.ts:27

unpinned.sort((a, b) => b.created - a.created || a.id.localeCompare(b.id))

The || trick works but is subtle. compareSessionCreated in session-trim.ts uses an explicit if (created !== 0) return created pattern.

Suggestion: Use the explicit conditional pattern for consistency and readability across all creation-time comparators.

Comment thread packages/app/src/context/global-sync/session-trim.ts Outdated
Comment thread packages/app/src/pages/layout/helpers.ts
Comment thread packages/app/src/pages/layout.tsx
Comment thread packages/opencode/src/session/session.ts
Comment thread packages/app/src/pages/layout/pawwork-session-nav.ts
@Astro-Han Astro-Han force-pushed the codex/fix-session-order branch from e218df9 to 426c149 Compare April 22, 2026 13:04
@Astro-Han Astro-Han merged commit 68ec9cc into dev Apr 22, 2026
21 checks passed
@Astro-Han Astro-Han deleted the codex/fix-session-order branch April 22, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P3 Low priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant