Skip to content

feat(app): sort sidebar sessions by user activity#476

Merged
Astro-Han merged 3 commits intodevfrom
codex/i473-sidebar-activity
May 6, 2026
Merged

feat(app): sort sidebar sessions by user activity#476
Astro-Han merged 3 commits intodevfrom
codex/i473-sidebar-activity

Conversation

@Astro-Han
Copy link
Copy Markdown
Owner

@Astro-Han Astro-Han commented May 6, 2026

Summary

  • Add sort=activity to the global experimental session list contract.
  • Compute session activity from the latest real user message time, falling back to session creation time.
  • Use activity-selected sessions for the PawWork sidebar initial window and row timestamp sorting.
  • Regenerate the v2 SDK types for the new global session fields and sort mode.

Why

The PawWork sidebar first window was still selected by session creation time. PR #471 fixed timestamps for rows already loaded into the sidebar, but an older session with recent user activity could still be hidden outside the initial window. This PR moves the selection contract to the backend/API layer so the first sidebar window represents conversations the user most recently typed into.

Related Issue

Closes #473

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.

Review Focus

Please check the activity-order cursor semantics in Session.listGlobal, especially same-time pagination and the rule that assistant-only messages, session.time.updated, compaction messages, and synthetic-only user messages do not promote a session. Real user messages that also carry synthetic reminder parts should still count as user activity.

Risk Notes

Medium. This adds a new global session list sort mode and uses correlated message/part queries for activity ordering. Existing updated and created sort modes remain covered by tests and unchanged for existing clients. No persistence migration, dependency, packaging, updater, signing, or permission changes are included.

How To Verify

Dependency install: bun install --frozen-lockfile -> completed successfully
SDK build: bun --cwd packages/sdk/js build -> generated v2 SDK output successfully
Backend focused tests: bun --cwd packages/opencode test --timeout 30000 ./test/server/global-session-activity-list.test.ts ./test/server/global-session-list.test.ts -> 21 pass, 0 fail
App focused tests: bun --cwd packages/app test --preload ./happydom.ts ./src/pages/layout/pawwork-sidebar-session-time.test.ts ./src/pages/layout/pawwork-sidebar-session-rows.test.ts ./src/pages/layout/pawwork-session-window.test.ts -> 927 pass, 0 fail
Opencode typecheck: bun --cwd packages/opencode typecheck -> completed successfully
App typecheck: bun --cwd packages/app typecheck -> completed successfully
Workspace typecheck: bun turbo typecheck -> 8 successful, 8 total
Diff check: git diff --check -> no whitespace errors
Desktop focused CI timeout check: bun --cwd packages/desktop-electron test --timeout 180000 ./src/main/embedded-server-build.test.ts -> 1 pass, 0 fail
Desktop CI shard: bun turbo test:ci --filter=@opencode-ai/desktop-electron -> 344 pass, 0 fail
Desktop startup check: bun run dev:desktop -> main/preload/renderer built and Electron app reached server ready; stopped the long-running dev process afterward

Screenshots or Recordings

Not captured. This changes sidebar data ordering, not layout or copy. The ordering contract is covered by backend route tests and frontend helper tests; Electron startup was verified.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, primary area, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • New Features

    • Sessions now sorted by recent activity instead of creation time, providing better visibility of your most-active sessions.
  • Refactor

    • Session window handling updated to leverage activity timestamp tracking for improved session organization.
  • Tests

    • New test coverage for activity-based session ordering and pagination behavior.

@Astro-Han Astro-Han added enhancement New feature or request P2 Medium priority app Application behavior and product flows labels May 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Warning

Rate limit exceeded

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

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 05757e77-c662-473f-89f6-81479a34ef2b

📥 Commits

Reviewing files that changed from the base of the PR and between 5ecc0d9 and 76740c9.

📒 Files selected for processing (6)
  • packages/app/src/pages/layout/pawwork-project-labels.test.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-session-rows.test.ts
  • packages/app/src/pages/layout/pawwork-sidebar-session-sort.test.ts
  • packages/app/src/pages/layout/pawwork-sidebar-session-time.test.ts
📝 Walkthrough

Walkthrough

This PR adds activity-based session ordering end-to-end: backend list endpoints and cursors now support an "activity" sort (based on last user-message timestamps), session rows expose activity metadata, and frontend window/state and sidebar time logic are updated to consume and prefer activity timestamps when selecting and rendering the initial Pawwork window.

Changes

Activity-Based Session Ordering (single cross-stack DAG)

Layer / File(s) Summary
Data Shape / Types
packages/opencode/src/session/session.ts, packages/app/src/pages/layout/pawwork-session-window.ts
Add activityAt and lastUserMessageAt to GlobalInfo/rows; export new PawworkWindowSession = `Session & Pick<GlobalSession, "activityAt"
Core Backend Implementation
packages/opencode/src/session/session.ts
Introduce SQL expressions lastUserMessageAtExpr and activityAtExpr, add activity sort path to list/listGlobal flows, compute and emit activityAt/lastUserMessageAt for activity-mode listing, and wire activity-aware ordering and pagination.
Backend API Cursor & Experimental Route Wiring
packages/opencode/src/server/instance/experimental.ts
Add ActivitySessionCursor zod schema and encode/decode helpers; extend experimental session-list route to accept sort=activity, decode activity cursors, and emit activity-based next-cursor headers.
Frontend Data Loading & State Shape
packages/app/src/pages/layout.tsx, packages/app/src/pages/layout/pawwork-session-window.ts
Change pawwork window state to use PawworkWindowSession[] for normal/pinned and `PawworkWindowSession
Frontend Time/Row Logic
packages/app/src/pages/layout/pawwork-session-source.ts, packages/app/src/pages/layout/pawwork-session-source.test.ts
Extend SessionTimeLike with activityAt?: number; add PartTimeLike and helpers (isActivityEligibleUserMessage, latestLoadedUserMessageTime). Update pawworkSidebarSessionTime to prefer finite activityAt and merge with loaded-user times; pass partsForMessage through build path. Add tests covering precedence rules (API activity vs loaded message parts, synthetic parts handling).
Frontend Sorting & Window Utilities
packages/app/src/pages/layout/pawwork-session-window.ts, packages/app/src/pages/layout/pawwork-session-window.test.ts
Add sessionActivityTime and byActivityDesc comparator; update sortPawworkSessionWindowSessions, mergeSessionsByID, and buildPawworkSessionWindow to operate on PawworkWindowSession[] and sort by activity descending with stable tiebreakers. Update tests to assert activity-based ordering and tie/pagination behavior.
Tests: Backend & Integration
packages/opencode/test/server/global-session-activity-list.test.ts, packages/opencode/test/server/global-session-list.test.ts
Add comprehensive tests verifying activity-order global listing: ordering by latest user message, ignoring assistant/compaction/synthetic-only messages, pagination with tied activity times, and HTTP experimental route round-trip for activity cursors. Minor test added to ensure object cursors are ignored for default ordering.
sequenceDiagram
  autonumber
  participant Frontend as Client (UI)
  participant API as Server (opencode)
  participant DB as Database
  rect rgba(100,150,240,0.5)
    Frontend->>API: GET /sessions?sort=activity&limit=N
    API->>DB: SELECT ... activityAtExpr, lastUserMessageAtExpr ... ORDER BY activityAt DESC, id DESC
    DB-->>API: rows with activityAt, lastUserMessageAt
    API->>API: encodeActivitySessionCursor(nextRow.activityAt, nextRow.id)
    API-->>Frontend: 200 OK (rows), x-next-cursor: encodedActivityCursor
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Astro-Han/pawwork#367: Related prior work on pawworkSidebarSessionTime that this change extends to prefer activityAt and parts-aware logic.
  • Astro-Han/pawwork#386: Modifies pawwork window utilities and tests; overlaps with the window-sorting and merge logic updated here.

Suggested labels

ui

Poem

🐰
I hop through code both near and far,
I sniff the freshest message-star;
New activity gets top-bunny spot,
Old dates tumble — that's my plot!
Hops and cheers for tidy sort.

🚥 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 'feat(app): sort sidebar sessions by user activity' clearly and concisely summarizes the main change: enabling activity-based sorting for sidebar sessions.
Linked Issues check ✅ Passed All objectives from issue #473 are met: activity-based sorting by latest user message time, session creation fallback, activity-eligible message filtering, stable pagination tie-breaking, backend API support via sort=activity, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing activity-based session sorting; no unrelated refactors, dependency upgrades, or extraneous modifications are present.
Description check ✅ Passed The pull request description is comprehensive and complete, providing clear summary, motivation, risk assessment, and thorough verification steps.

✏️ 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/i473-sidebar-activity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

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

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 introduces a new "activity" sorting mode for sessions, which prioritizes the time of the latest user message over the session creation time. Changes include backend SQL updates to calculate activity timestamps, API route enhancements for sorting and cursor-based pagination, and frontend updates to utilize this new sorting logic in the session layout. Feedback suggests improving the robustness of cursor validation for updated-time sorting and simplifying the logic for mapping user message timestamps in the session list.

Comment thread packages/opencode/src/session/session.ts Outdated
Comment thread packages/opencode/src/session/session.ts
@Astro-Han Astro-Han force-pushed the codex/i473-sidebar-activity branch from 40203a3 to 622fb2f Compare May 6, 2026 11:47
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 (3)
packages/app/src/pages/layout.tsx (2)

596-609: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pinned/active fallback loads drop the activity timestamp.

Anything not already present in the sort: "activity" page is fetched through session.get(), so pinned/active entries here can enter the window without activityAt. Those rows then fall back to cache or time.created, which breaks the new “same activity source for ordering and row timestamps” rule for exactly the sessions this block backfills.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/layout.tsx` around lines 596 - 609, The pinned/active
fallbacks lose the activity timestamp because sessions fetched via
loadSessionByID (or session.get()) may lack time.activityAt; when building
pinned and active from loaded.get(id) ?? (await loadSessionByID(id)) preserve or
inject the activity timestamp from the original response-loaded session: use the
loaded Map (loaded) entry's time.activityAt (from response.data /
PawworkWindowSession) to set time.activityAt on the fallback session if it is
missing. Apply the same preservation when constructing active (params.id) so
loadSessionByID results inherit activityAt from loaded.get(params.id) if
present.

646-659: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve activity metadata when applying session.updated.

info is a plain Session, so replacing an existing PawworkWindowSession with it drops activityAt and lastUserMessageAt. After any session.updated event, that row can immediately fall back to creation-time ordering until the next full reload.

Suggested fix
   const upsertPawworkWindowSession = (info: Session) => {
     if (info.parentID || info.time?.archived) return
+    const mergeWindowSession = (current: PawworkWindowSession[]) => {
+      const existing = current.find((session) => session.id === info.id)
+      const next: PawworkWindowSession = {
+        ...info,
+        activityAt: existing?.activityAt,
+        lastUserMessageAt: existing?.lastUserMessageAt,
+      }
+      return sortPawworkSessionWindowSessions([...current.filter((session) => session.id !== info.id), next])
+    }
     batch(() => {
-      setPawworkSessionWindowState("normal", (current) =>
-        sortPawworkSessionWindowSessions([...current.filter((session) => session.id !== info.id), info]),
-      )
+      setPawworkSessionWindowState("normal", mergeWindowSession)
       if (store.pawworkPinnedSessions.includes(info.id)) {
-        setPawworkSessionWindowState("pinned", (current) =>
-          sortPawworkSessionWindowSessions([...current.filter((session) => session.id !== info.id), info]),
-        )
+        setPawworkSessionWindowState("pinned", mergeWindowSession)
       }
       if (params.id === info.id) {
-        setPawworkSessionWindowState("active", info)
+        setPawworkSessionWindowState("active", (current) =>
+          current?.id === info.id
+            ? {
+                ...info,
+                activityAt: current.activityAt,
+                lastUserMessageAt: current.lastUserMessageAt,
+              }
+            : info,
+        )
       }
     })
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/layout.tsx` around lines 646 - 659,
upsertPawworkWindowSession is replacing existing PawworkWindowSession entries
with the plain Session `info`, which drops derived fields like activityAt and
lastUserMessageAt; change the insertion logic in setPawworkSessionWindowState
calls (and the active/pinned branches) to merge `info` with any existing session
object (lookup by id) so you preserve existing activityAt and lastUserMessageAt
when updating (i.e., find existing session from `current` before filter and use
{ ...existing, ...info } or similar) so session.updated does not wipe those
metadata fields.
packages/app/src/pages/layout/pawwork-session-source.ts (1)

72-81: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't let a stale activityAt hide fresher loaded user input.

Line 73 turns activityAt into a hard override, but that value is only as fresh as the last global-list fetch. If the current session gets a newer local user message before the window reloads, the sidebar will keep sorting/displaying the older timestamp instead of the latest known user activity.

Suggested fix
 export function pawworkSidebarSessionTime(session: SessionTimeLike, messages?: MessageTimeLike[]) {
-  if (isFiniteNumber(session.activityAt)) return session.activityAt
+  let latestLoadedUserAt: number | undefined
   for (let i = (messages?.length ?? 0) - 1; i >= 0; i--) {
     const message = messages?.[i]
     if (message?.role !== "user") continue
     const created = message.time?.created
-    if (isFiniteNumber(created)) return created
+    if (isFiniteNumber(created)) {
+      latestLoadedUserAt = created
+      break
+    }
   }
+  if (isFiniteNumber(session.activityAt)) {
+    return latestLoadedUserAt === undefined ? session.activityAt : Math.max(session.activityAt, latestLoadedUserAt)
+  }
+  if (latestLoadedUserAt !== undefined) return latestLoadedUserAt
   const sessionCreated = session.time?.created
   return isFiniteNumber(sessionCreated) ? sessionCreated : 0
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/layout/pawwork-session-source.ts` around lines 72 -
81, The function pawworkSidebarSessionTime currently returns session.activityAt
unconditionally if it's finite, which can hide a newer local user message;
change the logic to compare timestamps: find the latest user message timestamp
(iterate messages for message.role === "user" and check message.time?.created),
then if session.activityAt is finite and >= that latest user timestamp return
session.activityAt, otherwise return the latest user timestamp if finite, else
fallback to session.time?.created (if finite) or 0; keep using isFiniteNumber
for all timestamp checks to locate and update the logic around
session.activityAt, messages, message.time?.created, and session.time?.created
in pawworkSidebarSessionTime.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/session/session.ts`:
- Around line 1037-1052: sessionOrder currently treats any non-"created" sort as
"updated", causing list({ sort: "activity" }) to be silently wrong; update
sessionOrder to explicitly handle the "activity" case instead of defaulting to
updated: add a branch for sort === "activity" that returns the correct ordering
(e.g. [desc(SessionTable.activity_at), desc(SessionTable.id)]) so Session.list()
and listGlobal() behave as advertised by ListSort, or if the activity column is
not present, throw a clear error when sort === "activity" so callers get
explicit feedback.

---

Outside diff comments:
In `@packages/app/src/pages/layout.tsx`:
- Around line 596-609: The pinned/active fallbacks lose the activity timestamp
because sessions fetched via loadSessionByID (or session.get()) may lack
time.activityAt; when building pinned and active from loaded.get(id) ?? (await
loadSessionByID(id)) preserve or inject the activity timestamp from the original
response-loaded session: use the loaded Map (loaded) entry's time.activityAt
(from response.data / PawworkWindowSession) to set time.activityAt on the
fallback session if it is missing. Apply the same preservation when constructing
active (params.id) so loadSessionByID results inherit activityAt from
loaded.get(params.id) if present.
- Around line 646-659: upsertPawworkWindowSession is replacing existing
PawworkWindowSession entries with the plain Session `info`, which drops derived
fields like activityAt and lastUserMessageAt; change the insertion logic in
setPawworkSessionWindowState calls (and the active/pinned branches) to merge
`info` with any existing session object (lookup by id) so you preserve existing
activityAt and lastUserMessageAt when updating (i.e., find existing session from
`current` before filter and use { ...existing, ...info } or similar) so
session.updated does not wipe those metadata fields.

In `@packages/app/src/pages/layout/pawwork-session-source.ts`:
- Around line 72-81: The function pawworkSidebarSessionTime currently returns
session.activityAt unconditionally if it's finite, which can hide a newer local
user message; change the logic to compare timestamps: find the latest user
message timestamp (iterate messages for message.role === "user" and check
message.time?.created), then if session.activityAt is finite and >= that latest
user timestamp return session.activityAt, otherwise return the latest user
timestamp if finite, else fallback to session.time?.created (if finite) or 0;
keep using isFiniteNumber for all timestamp checks to locate and update the
logic around session.activityAt, messages, message.time?.created, and
session.time?.created in pawworkSidebarSessionTime.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 58b11065-a2b6-4982-a44f-1e021018fe12

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5ce8a and 622fb2f.

⛔ 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 (8)
  • packages/app/src/pages/layout.tsx
  • 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-session-window.test.ts
  • packages/app/src/pages/layout/pawwork-session-window.ts
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/src/session/session.ts
  • packages/opencode/test/server/global-session-activity-list.test.ts

Comment thread packages/opencode/src/session/session.ts Outdated
@Astro-Han Astro-Han force-pushed the codex/i473-sidebar-activity branch 5 times, most recently from d6910f5 to df70d17 Compare May 6, 2026 12:46
@Astro-Han Astro-Han force-pushed the codex/i473-sidebar-activity branch from df70d17 to 5ecc0d9 Compare May 6, 2026 13:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/opencode/src/session/session.ts (1)

1055-1094: Indexes are already in place; consider a CTE optimization for future performance tuning.

lastUserMessageAtExpr is a correlated scalar subquery with nested NOT EXISTS clauses against PartTable. Because Drizzle inlines SQL-fragment objects at every call site, the activity-sort path with a cursor evaluates this subquery five times per session row: twice in SELECT (for activityAt and lastUserMessageAt), twice in the WHERE OR-branches (inside lt(…) and eq(…) which both wrap activityAtExpr), and once in ORDER BY.

The required indexes are already present in the schema:

  • MessageTable: composite index on (session_id, time_created, id) — which is even more specific than the minimum needed
  • PartTable: indexes on (message_id, id) and (session_id) covering both NOT EXISTS paths

To further optimize if this becomes a bottleneck, consider wrapping the correlated computation in a SQL CTE or subquery (using Drizzle's .as() or a raw WITH clause) so SQLite computes activityAt once per row and reuses it by name in ORDER BY and WHERE, avoiding redundant evaluations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/session/session.ts` around lines 1055 - 1094, The
correlated scalar subquery lastUserMessageAtExpr is being inlined multiple times
(for activityAtExpr and in WHERE/ORDER BY), causing redundant evaluations;
refactor to compute it once via a CTE or named subquery and reference that name
for activityAt/activityAtExpr/lastUserMessageAt in activitySelect and in any
lt(...)/eq(...) or ORDER BY usage so SQLite reuses the single computed value per
Session row (use Drizzle's .as() or a raw WITH to create the CTE that selects
session id plus lastUserMessageAt from MessageTable/PartTable, then join/consume
that CTE when building activitySelect).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/pages/layout.tsx`:
- Around line 629-632: Pinned/active sessions fetched via loaded.get or
loadSessionByID are missing activity metadata (activityAt/lastUserMessageAt),
causing ordering issues; update the logic so IDs not present in response.data
are hydrated through an activity-aware endpoint or a per-session fetch that
returns the same metadata instead of plain Session: change calls to
loadSessionByID to use (or implement) a loadSessionWithActivityByID or a
fetchSessionsActivityByIDs and then pass that enriched session into
mergePawworkWindowSessionMetadata (and likewise update the identical block at
637-640) so existing.get(id) is merged with full activity-aware session objects.

In `@packages/app/src/pages/layout/pawwork-session-source.ts`:
- Around line 94-97: The no-activityAt fallback currently picks the newest
loaded user message without checking part-based eligibility, so update the
fallback logic that selects the newest user message to also call
partsForMessage(message.id) and pass that into isActivityEligibleUserMessage; if
the check returns false, skip/continue and try the next message. Apply the same
change to the other fallback occurrence that uses the newest loaded message (the
second block that mirrors this logic) so both selection sites honor
requireEligibility by consulting partsForMessage and
isActivityEligibleUserMessage before promoting a message.

---

Nitpick comments:
In `@packages/opencode/src/session/session.ts`:
- Around line 1055-1094: The correlated scalar subquery lastUserMessageAtExpr is
being inlined multiple times (for activityAtExpr and in WHERE/ORDER BY), causing
redundant evaluations; refactor to compute it once via a CTE or named subquery
and reference that name for activityAt/activityAtExpr/lastUserMessageAt in
activitySelect and in any lt(...)/eq(...) or ORDER BY usage so SQLite reuses the
single computed value per Session row (use Drizzle's .as() or a raw WITH to
create the CTE that selects session id plus lastUserMessageAt from
MessageTable/PartTable, then join/consume that CTE when building
activitySelect).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 03d7a065-fe0a-40a3-a70d-aa0c1a35e810

📥 Commits

Reviewing files that changed from the base of the PR and between 622fb2f and 5ecc0d9.

⛔ 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 (9)
  • packages/app/src/pages/layout.tsx
  • 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-session-window.test.ts
  • packages/app/src/pages/layout/pawwork-session-window.ts
  • packages/opencode/src/server/instance/experimental.ts
  • packages/opencode/src/session/session.ts
  • packages/opencode/test/server/global-session-activity-list.test.ts
  • packages/opencode/test/server/global-session-list.test.ts

Comment thread packages/app/src/pages/layout.tsx
Comment thread packages/app/src/pages/layout/pawwork-session-source.ts Outdated
@Astro-Han Astro-Han merged commit b917193 into dev May 6, 2026
21 checks passed
@Astro-Han Astro-Han deleted the codex/i473-sidebar-activity branch May 6, 2026 14:49
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 enhancement New feature or request P2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Sort initial sidebar sessions by last user message time

1 participant