Skip to content

feat(sidebar): persisted project-level last-activity timestamp#160

Merged
cnjack merged 1 commit into
mainfrom
feat/project-last-updated
Jul 20, 2026
Merged

feat(sidebar): persisted project-level last-activity timestamp#160
cnjack merged 1 commit into
mainfrom
feat/project-last-updated

Conversation

@cnjack

@cnjack cnjack commented Jul 20, 2026

Copy link
Copy Markdown
Owner

需求

给 conversation 的 project 加上 last update 时间;删除 conversation 时 project 的时间不变、排序不变。

实现

存储:在 session index 旁新增 projects.json sidecar 文件(project path → last-activity timestamp)。不嵌入 session.json,是因为共享 index 可能被旧版本二进制重写而静默丢掉嵌入字段(桌面 sidecar + CLI 混版场景);sidecar 文件旧版本完全不碰,天然免疫。

写入时机(均为单调 max,按解析后的 RFC3339 瞬时比较而非字符串——数据混有本地时区偏移与 UTC,字符串比较在跨时区时会出错):

  • 会话创建(addToIndex)→ 用会话 StartTime 触碰 project 时间
  • 会话真实活动(UpdateSessionMeta 中 UpdatedAt 变化,即一轮对话开始/结束)→ 同步触碰
  • pin/archive/rename 等元数据编辑不触碰(UpdatedAt 本身就不动)
  • 删除会话绝不回退 project 时间 → 删除最新的会话不会让 project 在侧栏排序中下沉

API:新增 GET /api/projects 返回 [{path, updated_at}]

前端

  • session slice 新增 projectTimes;启动时 loadProjects 拉取(与 tasks 并行),采用 per-key 单调 max 合并(避免覆盖 in-flight 的实时触碰)
  • task_status WS 事件现在携带 project 路径 + 服务端写入的精确时间戳,前端用服务端值实时触碰(不用浏览器时钟)
  • Sidebar project 分组排序优先使用持久化时间戳(缺失时回退到子会话派生值,兼容旧数据),project 头部显示相对时间

验证

  • go test -race ./internal/session/ ./internal/web/ 通过;新增生命周期契约测试(含混合时区单调性、legacy 回退、删除/改名 round-trip)与 GET /api/projects handler 测试
  • golangci-lint 0 issues;make lint-web 通过
  • 沙箱 HOME 端到端验证:删除某 project 最新会话后,projects.json 时间戳不变,侧栏该 project 仍显示原相对时间且排序不变(子会话只剩 10d 旧任务时,project 头仍显示 19h 并保持置顶)

对抗评审

已经过一轮对抗评审(adversarial review),修复了:

  1. blocker — RFC3339 混合时区偏移下字符串比较错误(全部改为解析瞬时比较)
  2. major — 旧版本二进制重写 index 会丢失嵌入的 projects 字段(改为 sidecar 文件)
  3. minor — setProjectTimes 全量替换可能覆盖实时触碰(改为单调 max 合并)
  4. minor — WS 事件不携带 project,自动化任务无法实时触碰(task_status 现在携带 project + 服务端时间戳,同时修复浏览器时钟偏差问题)

Summary by CodeRabbit

  • New Features

    • Added project-level activity tracking to preserve accurate sidebar ordering across sessions.
    • Project groups now display their latest activity time in the sidebar.
    • Added project listing support for loading project paths and activity timestamps.
    • Live task updates now refresh project activity immediately.
    • Legacy installations without project metadata continue to display an empty project list.
  • Bug Fixes

    • Project ordering now handles timestamps consistently across time zones.
    • Deleting sessions no longer causes project activity times to move backward.

Projects now carry their own last-activity timestamp, bumped on session
create and on real turns (session UpdatedAt moves), and never rolled
back on delete. The sidebar sorts project groups by this timestamp
(falling back to deriving from child sessions when absent) and shows a
relative time in the project header — so deleting a conversation no
longer changes its project's time or reorders the project list.

Storage: a projects.json sidecar next to the session index (NOT an
embedded key in session.json — an older binary rewriting the shared
index would silently drop an embedded section on mixed-version
installs; a sidecar it never touches is immune). All comparisons are on
parsed RFC3339 instants, not strings: the data mixes server-local
offsets with UTC, and lexicographic order breaks across offsets.

- session: ProjectMeta + projects.json (atomic private writes under
  indexMu), monotonic-max touch on addToIndex / UpdateSessionMeta,
  untouched on DeleteSessionByUUID, ListProjectMeta accessor
- web: GET /api/projects endpoint; task_status WS events now carry the
  project path + the exact server-side timestamp being persisted
- web UI: projectTimes in the session slice (merge-on-load, live touch
  from task_status with the server's values), project header time +
  delete-stable ordering in the Sidebar
- tests: lifecycle contract incl. mixed-offset monotonicity, legacy
  fallback, delete/title round-trip; GET /api/projects handler
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e24c052-757c-401a-8ef4-13732f5ad78c

📥 Commits

Reviewing files that changed from the base of the PR and between 0cbec24 and 198eb17.

📒 Files selected for processing (12)
  • internal/session/index_test.go
  • internal/session/session.go
  • internal/web/engine.go
  • internal/web/projects_test.go
  • internal/web/server.go
  • internal/web/sessions.go
  • web/src/app/store.ts
  • web/src/app/wsBridge.ts
  • web/src/components/Sidebar.tsx
  • web/src/lib/api.ts
  • web/src/lib/types.ts
  • web/src/lib/ws.ts

📝 Walkthrough

Walkthrough

Project activity timestamps are persisted in projects.json, exposed through a new HTTP endpoint and task-status WebSocket fields, merged into frontend state, and used to order and annotate sidebar project groups.

Changes

Project activity metadata

Layer / File(s) Summary
Persist project activity metadata
internal/session/session.go, internal/session/index_test.go
Adds atomic projects.json persistence, monotonic RFC3339 timestamp updates, deletion preservation, metadata listing, and lifecycle coverage.
Expose project timestamps
internal/web/engine.go, internal/web/sessions.go, internal/web/server.go, internal/web/projects_test.go
Adds project metadata to task-status events and registers GET /api/projects, including legacy and populated response tests.
Merge project activity updates
web/src/lib/types.ts, web/src/lib/api.ts, web/src/lib/ws.ts, web/src/app/store.ts, web/src/app/wsBridge.ts
Loads project timestamps over HTTP and applies monotonic updates from HTTP snapshots and WebSocket activity events.
Order and render project activity
web/src/components/Sidebar.tsx
Uses persisted timestamps for project ordering and displays relative project activity times with session-activity fallback.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SessionEngine
  participant WebSocket
  participant WSBridge
  participant SessionStore
  participant Sidebar
  SessionEngine->>WebSocket: task_status with project and updated_at
  WebSocket->>WSBridge: forward task status metadata
  WSBridge->>SessionStore: touchProjectTime(path, updatedAt)
  SessionStore->>Sidebar: updated projectTimes
  Sidebar->>Sidebar: reorder groups and render relative time
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: persisted project-level last-activity timestamps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/project-last-updated

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.

@cnjack
cnjack merged commit e62d81c into main Jul 20, 2026
4 checks passed
@cnjack
cnjack deleted the feat/project-last-updated branch July 20, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant