Skip to content

fix: align prefix session monitoring and usage logs - #1375

Merged
ding113 merged 9 commits into
devfrom
prefix-affinity-session-monitoring
Aug 2, 2026
Merged

fix: align prefix session monitoring and usage logs#1375
ding113 merged 9 commits into
devfrom
prefix-affinity-session-monitoring

Conversation

@ding113

@ding113 ding113 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Use observed canonical prefix identities and observed concurrency when reporting active sessions, so in-flight prefix-affinity sessions are shown as active.
  • Keep client-provided session IDs as the copyable usage-log value while exposing canonical prefix IDs and all grouped client IDs in tooltips.
  • Match usage-log session filters and suggestions against both canonical and physical/client session IDs.
  • Align activity-stream grouping and replay-safe fallback queries with canonical identities.

Tests

  • TDD coverage added for active-session monitoring, activity stream identity matching, session filters/suggestions, and both usage-log table variants.
  • bun run typecheck
  • bun run lint
  • bun run test
  • bun run build

Target

  • Base branch: dev

Greptile Summary

The PR aligns usage-log filtering, suggestions, display metadata, active-session monitoring, and replay behavior around canonical prefix identities while preserving client-provided IDs.

  • Matches both canonical and physical session IDs in usage-log queries and suggestions.
  • Hydrates grouped client IDs separately from paginated log projections.
  • Adds session-identity indexes, migration preflight handling, and regression coverage.
  • Updates the container build runtime and dependency requirements.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/repository/usage-logs.ts Reworks canonical/physical session filtering, suggestions, and source-ID hydration; the earlier suggestion-limit defect is fixed.
src/repository/_shared/usage-log-filters.ts Extends exact session matching to physical IDs while protecting reserved canonical identity prefixes.
src/actions/active-sessions.ts Aligns active-session reporting with observed canonical prefix identities and concurrency.
src/repository/activity-stream.ts Aligns activity grouping and replay lookup behavior with canonical session identities.
src/drizzle/schema.ts Adds the ledger session-identity expression index represented by the generated migration.

Reviews (10): Last reviewed commit: "test: strengthen session identity regres..." | Re-trigger Greptile

Context used:

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

会话身份与活动状态

Layer / File(s) Summary
日志会话身份聚合与查询
src/repository/usage-logs.ts, src/repository/_shared/usage-log-filters.ts, tests/unit/repository/usage-logs-sessionid-*.test.ts, tests/unit/repository/usage-logs-replay-projection.test.ts
使用日志聚合物理会话 ID。筛选、详情查询、统计查询和会话建议同时匹配规范会话身份与物理会话 ID。
活动流观察会话处理
src/repository/activity-stream.ts, tests/unit/repository/activity-stream-replay.test.ts
活动流使用 getObservedActiveSessions(),并以合并后的会话标识执行查询和排除。
日志表会话标识展示
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx, src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx, src/app/api/v1/resources/usage-logs/handlers.ts, src/lib/api-client/v1/actions/usage-logs.ts, src/app/[locale]/dashboard/logs/_components/*.test.tsx, tests/api/v1/usage-logs/usage-logs.test.ts
API 和旧版转换保留 sourceSessionIdsByIdentity。日志表优先显示和复制来源会话 ID。Tooltip 列出来源会话 ID,并在必要时显示原始 sessionId
活跃会话并发状态
src/actions/active-sessions.ts, tests/unit/actions/active-sessions-monitoring.test.ts
getAllSessions在缓存和聚合路径中查询并发数。正并发数的会话返回 in_progress,并包含 concurrentCount
导出与运行时契约
src/actions/usage-logs.ts, tests/unit/actions/usage-logs-export-retry-count.test.ts, deploy/Dockerfile, package.json, tests/unit/deploy-dockerfile-contract.test.ts
导出查询关闭来源会话 ID回填。Docker 构建使用 Node.js trixie-slim 并复制 Bun。Node.js 最低版本更新为 >=22.19.0

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.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
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.
Title check ✅ Passed 标题简洁明确,准确概括了前缀会话监控和使用日志对齐这一主要变更。
Description check ✅ Passed 描述与变更相关,涵盖会话监控、使用日志、活动流、测试和构建环境更新。
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch prefix-affinity-session-monitoring

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.

Comment thread src/repository/usage-logs.ts Outdated
Comment on lines +1708 to +1714
.groupBy(messageSessionIdentity, messageRequest.sessionId)
.orderBy(desc(sql`min(${messageRequest.createdAt})`))
.limit(limit);

return results.map((r) => r.sessionId).filter((id): id is string => Boolean(id));
const suggestions = new Set<string>();
for (const row of results) {
if (row.sessionId) suggestions.add(row.sessionId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Suggestion limit drops matching IDs

When the term matches physical client IDs but not their canonical identities, this loop inserts each nonmatching canonical ID first and truncates the expanded results to limit, causing unrelated suggestions to displace the matching client session IDs.

Knowledge Base Used: Database Schema & Repository Layer

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/repository/usage-logs.ts
Line: 1708-1714

Comment:
**Suggestion limit drops matching IDs**

When the term matches physical client IDs but not their canonical identities, this loop inserts each nonmatching canonical ID first and truncates the expanded results to `limit`, causing unrelated suggestions to displace the matching client session IDs.

**Knowledge Base Used:** [Database Schema & Repository Layer](https://app.greptile.com/ygxz/-/custom-context/knowledge-base/ding113/claude-code-hub/-/docs/database-schema.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread src/repository/usage-logs.ts Outdated
Comment on lines +68 to +75
const messageSourceSessionIds = sql<string[]>`
ARRAY_AGG(${messageRequest.sessionId}) FILTER (WHERE ${messageRequest.sessionId} IS NOT NULL)
OVER (PARTITION BY ${messageSessionIdentity})
`;
const ledgerSourceSessionIds = sql<string[]>`
ARRAY_AGG(${usageLedger.sessionId}) FILTER (WHERE ${usageLedger.sessionId} IS NOT NULL)
OVER (PARTITION BY ${ledgerSessionIdentity})
`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Session arrays bypass pagination bounds

If a long-lived canonical identity accumulates many request or ledger rows, these window aggregates materialize its complete physical-ID array before pagination and attach it to every selected row, increasing database memory, query time, and response size as the session grows.

Knowledge Base Used: Database Schema & Repository Layer

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/repository/usage-logs.ts
Line: 68-75

Comment:
**Session arrays bypass pagination bounds**

If a long-lived canonical identity accumulates many request or ledger rows, these window aggregates materialize its complete physical-ID array before pagination and attach it to every selected row, increasing database memory, query time, and response size as the session grows.

**Knowledge Base Used:** [Database Schema & Repository Layer](https://app.greptile.com/ygxz/-/custom-context/knowledge-base/ding113/claude-code-hub/-/docs/database-schema.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🤖 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 `@src/app/`[locale]/dashboard/logs/_components/usage-logs-table.tsx:
- Around line 197-212: In
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx lines 197-212,
create one fallback source-ID list before rendering and use it for the canonical
sessionId includes check; apply the same change in
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx lines
849-864 so a fallback sourceSessionId is not displayed again as sessionId.

In `@src/repository/activity-stream.ts`:
- Around line 157-160: 在活动流查询构建逻辑中更新条件添加处,移除针对 messageRequest.sessionId 的
notInArray 条件,仅保留基于 messageSessionIdentity 和 excludedSessionIds 的排除判断,确保 NULL
的物理 session ID 不会因 SQL 三值逻辑被过滤。

In `@src/repository/usage-logs.ts`:
- Around line 68-75: 在 usage log 查询结果的共享映射层统一去重 sourceSessionIds,覆盖
findUsageLogsBatch、findUsageLogsWithDetails 及其他查询路径;确保每个物理 session ID
只保留一个元素,可通过使 ARRAY_AGG 聚合唯一或在映射时使用 Set,并保持非空过滤行为不变。

In `@tests/unit/actions/active-sessions-monitoring.test.ts`:
- Around line 53-54: 更新测试 fixture 中的 firstRequestAt 和
lastRequestAt,改用相对于当前测试时钟的时间,或在相关测试中通过 Vitest 固定系统时间;确保 getAllSessions 执行时
fixture 不会因固定日期超过五分钟而被归类为 inactive。
🪄 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: CHILL

Plan: Pro Plus

Run ID: 11a6c30d-fb0e-4e63-9357-bd96f4a1b0e1

📥 Commits

Reviewing files that changed from the base of the PR and between ec9423f and b7b1876.

📒 Files selected for processing (12)
  • src/actions/active-sessions.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/repository/_shared/usage-log-filters.ts
  • src/repository/activity-stream.ts
  • src/repository/usage-logs.ts
  • tests/unit/actions/active-sessions-monitoring.test.ts
  • tests/unit/repository/activity-stream-replay.test.ts
  • tests/unit/repository/usage-logs-sessionid-filter.test.ts
  • tests/unit/repository/usage-logs-sessionid-suggestions.test.ts

Comment thread src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx Outdated
Comment thread src/repository/activity-stream.ts Outdated
Comment thread src/repository/usage-logs.ts Outdated
Comment thread tests/unit/actions/active-sessions-monitoring.test.ts
@ding113
ding113 force-pushed the prefix-affinity-session-monitoring branch from b7b1876 to a6acd60 Compare August 1, 2026 17:37

@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: b7b187627e

ℹ️ 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 src/repository/usage-logs.ts Outdated
Comment on lines +230 to +232
sourceSessionIds: sql<
string[]
>`ARRAY_AGG(${messageRequest.sessionId}) FILTER (WHERE ${messageRequest.sessionId} IS NOT NULL) OVER (PARTITION BY ${messageSessionIdentity})`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Deduplicate source sessions before attaching them

When a prefix identity accumulates many requests, this window aggregate adds one entry per request rather than one per physical session, and PostgreSQL must evaluate the filtered partition before applying the page limit. Consequently, a page of 50 rows for an identity with N requests can return roughly 50×N session-ID values; the tables then render the array directly, producing repeated tooltip entries, duplicate React keys, and potentially very large queries and API payloads. Resolve the distinct source IDs separately instead of attaching the full per-request window aggregate to every log row.

Useful? React with 👍 / 👎.

Comment thread src/repository/usage-logs.ts Outdated
Comment on lines +1706 to +1708
const results = await query
.where(and(...conditions))
.groupBy(messageSessionIdentity)
.groupBy(messageSessionIdentity, messageRequest.sessionId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply the limit after deduplicating suggestion identities

When a prefix identity has multiple physical session IDs, grouping by the canonical/source pair produces multiple SQL rows for that one identity, but the query applies limit before the pairs are expanded and deduplicated in JavaScript. A single canonical identity can therefore consume the entire query limit and hide other matching identities; expansion can also create up to twice the limit and the final slice arbitrarily discards later matching source IDs. Build a distinct ordered set of canonical and physical suggestions before applying the limit.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions github-actions Bot added the size/M Medium PR (< 500 lines) label Aug 1, 2026
Comment thread src/actions/active-sessions.ts Outdated
s.totalCacheReadTokens,
costUsd: s.totalCostUsd,
status: "completed",
status: (concurrentCounts.get(s.sessionId) ?? 0) > 0 ? "in_progress" : "completed",

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.

[HIGH] [LOGIC-BUG] In-progress sessions can still be returned in the inactive page

Why this is a problem: status now comes from getObservedConcurrentCountBatch(...), but this loop still buckets rows into active/inactive only by lastRequestAt. A request that has been running for more than five minutes will be marked in_progress here and still land in inactive, and the inactive table later zeroes concurrentCount, so the monitoring UI renders an actually busy session as idle.

Suggested fix:

const concurrentCount = concurrentCounts.get(s.sessionId) ?? 0;
const isCurrentlyActive = concurrentCount > 0 || lastRequestTime >= fiveMinutesAgo;

const sessionInfo: ActiveSessionInfo = {
  // ...
  status: concurrentCount > 0 ? "in_progress" : "completed",
  concurrentCount,
};

if (isCurrentlyActive) {
  active.push(sessionInfo);
} else {
  inactive.push(sessionInfo);
}

Comment thread src/repository/usage-logs.ts Outdated
createdAtRaw: sql<string>`to_char(${messageRequest.createdAt} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`,
sessionId: messageSessionIdentity,
sourceSessionId: messageRequest.sessionId,
sourceSessionIds: sql<

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.

[MEDIUM] [LOGIC-BUG] sourceSessionIds is duplicated once per request in the batch API

Why this is a problem: ARRAY_AGG(${messageRequest.sessionId}) OVER (PARTITION BY ...) collects every row in the identity partition, so repeated requests from the same client session produce repeated IDs. findUsageLogsBatch() returns that array unchanged, and the virtualized logs tooltip renders each entry, so a busy prefix-affinity session shows the same client session ID many times and inflates the response payload.

Suggested fix:

return {
  ...row,
  sourceSessionIds: row.sourceSessionIds ? [...new Set(row.sourceSessionIds)] : undefined,
  requestSequence: row.requestSequence ?? null,
  // ...
};

@github-actions github-actions 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.

Code Review Summary

I found two user-visible regressions in the new session-monitoring / usage-log identity work. Both are in changed code paths and affect the dashboard behavior directly: one can hide live sessions from the active list, and the other can flood the virtualized usage-log tooltip with duplicate client IDs.

PR Size: M

  • Lines changed: 326
  • Files changed: 12

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 1 1 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

  • None.

High Priority Issues (Should Fix)

  • [HIGH] [LOGIC-BUG] src/actions/active-sessions.ts:468 marks a session as in_progress from observed concurrency but still leaves active/inactive paging based only on lastRequestAt, so long-running requests can disappear from the active section and render as idle in the inactive table.
  • [MEDIUM] [LOGIC-BUG] src/repository/usage-logs.ts:230 adds sourceSessionIds to the batch API without normalizing duplicates, so the virtualized usage-log tooltip can show the same client session ID once per request and unnecessarily bloat the response.

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

@github-actions github-actions 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.

  • Reviewed PR #1375, applied the size/M label, and posted the review summary on GitHub.

  • Posted a high-priority inline comment on src/actions/active-sessions.ts:468 for a monitoring regression: sessions can be marked in_progress but still get paged into the inactive section, which makes long-running live sessions appear idle.

  • Posted a medium-severity inline comment on src/repository/usage-logs.ts:230 for duplicated sourceSessionIds in the cursor-based batch path, which can flood the virtualized usage-log tooltip with repeated client IDs and bloat the response.

  • Summary status: 1 high issue, 1 medium issue, no security or error-handling findings above threshold.

  • If you want, I can also draft the minimal code changes to address both findings.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (4)
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)

206-212: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

跨页合并使用覆盖语义。

Object.fromEntries 对同一 identity 保留最后一页的值,不做合并。当前仓储层按 identity 做全量聚合,各页返回的集合相同,因此显示结果正确。如果后续 hydration 改为限定在页面行范围内,覆盖会丢失前面页已获得的物理会话 ID。建议改为按 identity 做并集累加。

♻️ 建议改为并集累加
-  const sourceSessionIdsByIdentity = useMemo<Record<string, string[]>>(
-    () =>
-      Object.fromEntries(
-        pages?.flatMap((page) => Object.entries(page.sourceSessionIdsByIdentity ?? {})) ?? []
-      ),
-    [pages]
-  );
+  const sourceSessionIdsByIdentity = useMemo<Record<string, string[]>>(() => {
+    const merged = new Map<string, Set<string>>();
+    for (const page of pages ?? []) {
+      for (const [identity, ids] of Object.entries(page.sourceSessionIdsByIdentity ?? {})) {
+        const bucket = merged.get(identity) ?? new Set<string>();
+        for (const id of ids) bucket.add(id);
+        merged.set(identity, bucket);
+      }
+    }
+    return Object.fromEntries([...merged].map(([identity, ids]) => [identity, [...ids]]));
+  }, [pages]);
🤖 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 `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
around lines 206 - 212, Update the sourceSessionIdsByIdentity construction in
the useMemo callback to merge entries across all pages by identity, accumulating
the union of session ID arrays instead of letting Object.fromEntries overwrite
earlier pages. Preserve the existing empty-page fallback and return the same
Record<string, string[]> shape.
src/repository/usage-logs.ts (1)

156-209: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

合并两个来源会话 ID 聚合函数。

hydrateUsageLogSourceSessionIdsloadUsageLogSourceSessionIdsByIdentity 的取值、并行查询和去重逻辑完全相同,仅输出形式不同(行内字段与身份映射)。可以抽出一个返回 Map<string, string[]> 的内部函数,两个入口在其上做投影。这样能避免后续修改只落在一处。

🤖 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 `@src/repository/usage-logs.ts` around lines 156 - 209, 合并
hydrateUsageLogSourceSessionIds 和 loadUsageLogSourceSessionIdsByIdentity
中重复的会话来源 ID 收集、并行查询及去重逻辑,抽出一个返回 Map<string, string[]> 的内部辅助函数;保留两个现有入口的输出契约,分别将
Map 投影为行内 sourceSessionIds 字段和身份映射。
tests/unit/repository/activity-stream-replay.test.ts (1)

114-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

断言依赖精确的 SQL 文本。

expect(condition.sql).not.toContain('and "message_request"."session_id" not in') 绑定了 Drizzle 的引号风格与关键字大小写。若 Drizzle 调整 SQL 生成格式,该断言会静默通过而不再检测回归。建议改为对小写化后的 SQL 断言 not in 出现次数,或断言 session_id" not in 片段不存在。

🤖 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 `@tests/unit/repository/activity-stream-replay.test.ts` around lines 114 - 116,
Update the assertion in the boundary SQL test around condition to avoid
depending on Drizzle’s exact quoting and keyword formatting: use the already
lowercased SQL to verify the relevant not-in condition is absent, preferably by
checking the expected occurrence count or a stable session_id/not-in fragment.
tests/unit/repository/usage-logs-sessionid-suggestions.test.ts (1)

136-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

该测试未验证前缀过滤。

测试名称说明“只返回匹配前缀的候选”,但 mock 直接返回 client-session,与传入的 term 无关。数据库层的 LIKE 条件由 mock 绕过。建议改为断言 whereArgs 中包含 like 与转义后的 pattern,或把测试名称改为描述“合并两次候选查询结果”。

🤖 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 `@tests/unit/repository/usage-logs-sessionid-suggestions.test.ts` around lines
136 - 156, Update the test around findUsageLogSessionIdSuggestions so it
actually verifies prefix filtering by capturing the query’s whereArgs and
asserting they contain like with the escaped “client” prefix pattern; otherwise
rename the test to describe only merging candidate results. Prefer preserving
the current name and adding the database-condition assertion.
🤖 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.

Nitpick comments:
In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx:
- Around line 206-212: Update the sourceSessionIdsByIdentity construction in the
useMemo callback to merge entries across all pages by identity, accumulating the
union of session ID arrays instead of letting Object.fromEntries overwrite
earlier pages. Preserve the existing empty-page fallback and return the same
Record<string, string[]> shape.

In `@src/repository/usage-logs.ts`:
- Around line 156-209: 合并 hydrateUsageLogSourceSessionIds 和
loadUsageLogSourceSessionIdsByIdentity 中重复的会话来源 ID 收集、并行查询及去重逻辑,抽出一个返回
Map<string, string[]> 的内部辅助函数;保留两个现有入口的输出契约,分别将 Map 投影为行内 sourceSessionIds
字段和身份映射。

In `@tests/unit/repository/activity-stream-replay.test.ts`:
- Around line 114-116: Update the assertion in the boundary SQL test around
condition to avoid depending on Drizzle’s exact quoting and keyword formatting:
use the already lowercased SQL to verify the relevant not-in condition is
absent, preferably by checking the expected occurrence count or a stable
session_id/not-in fragment.

In `@tests/unit/repository/usage-logs-sessionid-suggestions.test.ts`:
- Around line 136-156: Update the test around findUsageLogSessionIdSuggestions
so it actually verifies prefix filtering by capturing the query’s whereArgs and
asserting they contain like with the escaped “client” prefix pattern; otherwise
rename the test to describe only merging candidate results. Prefer preserving
the current name and adding the database-condition assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 41c27649-2333-43b5-b936-a3c1f604f509

📥 Commits

Reviewing files that changed from the base of the PR and between a6acd60 and cf80980.

📒 Files selected for processing (15)
  • src/actions/active-sessions.ts
  • src/actions/usage-logs.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/lib/api-client/v1/actions/usage-logs.ts
  • src/repository/activity-stream.ts
  • src/repository/usage-logs.ts
  • tests/unit/actions/active-sessions-monitoring.test.ts
  • tests/unit/actions/usage-logs-export-retry-count.test.ts
  • tests/unit/repository/activity-stream-replay.test.ts
  • tests/unit/repository/usage-logs-replay-projection.test.ts
  • tests/unit/repository/usage-logs-sessionid-filter.test.ts
  • tests/unit/repository/usage-logs-sessionid-suggestions.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/actions/active-sessions.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx

@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: cf809808eb

ℹ️ 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".

};
return {
logs: page.logs ?? page.items ?? [],
sourceSessionIdsByIdentity: page.sourceSessionIdsByIdentity,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve grouped IDs in the REST response

When the dashboard loads cursor-paginated logs through /api/v1/usage-logs, this value is always undefined: toUsageLogsListResponse in src/app/api/v1/resources/usage-logs/handlers.ts reconstructs the action result using only items and pageInfo, dropping sourceSessionIdsByIdentity. Consequently, the virtualized table receives only each row's current sourceSessionId and cannot show the other physical client IDs grouped under the canonical prefix identity. Forward the map through that REST response before attempting to read it here.

Useful? React with 👍 / 👎.

Comment thread src/repository/activity-stream.ts Outdated
Comment on lines +108 to +109
inArray(messageSessionIdentity, activeSessionIds),
inArray(messageRequest.sessionId, activeSessionIds)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match observed sessions only by canonical identity

When a client supplies a physical ID equal to an active canonical prefix ID such as pfx:<scope>:<fingerprint>, this second predicate includes that unrelated physical session in the active-session query. buildPublicSessionIdentity deliberately remaps client-controlled pfx: IDs to the sid: namespace to prevent exactly this alias, but matching the raw sessionId here bypasses that isolation and can crowd genuine sessions out of the activity stream. Observed tracker entries are canonical identities, and the COALESCE predicate already matches ordinary physical sessions whose canonical identity is unchanged, so the raw-ID alternative should not be used.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions github-actions Bot mentioned this pull request Aug 1, 2026
4 tasks
@ding113
ding113 force-pushed the prefix-affinity-session-monitoring branch from cf80980 to e4d7030 Compare August 1, 2026 20:43

@coderabbitai coderabbitai 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.

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)
package.json (1)

40-40: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

移除 package.json 中的硬编码 CUI token。

Line 40 将认证值直接写入 cui script。该 script 同时绑定 0.0.0.0。拥有仓库内容的人员可以复用此值访问可达的 CUI 服务。

请立即撤销并轮换当前 token。改为从 CUI_TOKEN 环境变量读取。未设置 CUI_TOKEN 时应直接失败。

🤖 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 `@package.json` at line 40, Remove the hard-coded token from the package.json
cui script and update it to require the CUI_TOKEN environment variable, failing
immediately when the variable is unset while preserving the existing host and
port settings. Revoke and rotate the exposed token outside the script and
repository.
🤖 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 `@package.json`:
- Line 6: Align the Node runtime declared by the package.json engines.node
setting with the version used by deploy/Dockerfile.dev: either pin the Docker
base image to a Node release meeting >=22.19.0 or lower engines.node to the
runtime version actually supported, ensuring dependency installation and startup
remain compatible.

---

Outside diff comments:
In `@package.json`:
- Line 40: Remove the hard-coded token from the package.json cui script and
update it to require the CUI_TOKEN environment variable, failing immediately
when the variable is unset while preserving the existing host and port settings.
Revoke and rotate the exposed token outside the script and repository.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 86f0cba0-2ebf-4748-bfbe-880fb352406e

📥 Commits

Reviewing files that changed from the base of the PR and between cf80980 and e4d7030.

📒 Files selected for processing (21)
  • deploy/Dockerfile
  • package.json
  • src/actions/active-sessions.ts
  • src/actions/usage-logs.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/api/v1/resources/usage-logs/handlers.ts
  • src/lib/api-client/v1/actions/usage-logs.ts
  • src/repository/_shared/usage-log-filters.ts
  • src/repository/activity-stream.ts
  • src/repository/usage-logs.ts
  • tests/api/v1/usage-logs/usage-logs.test.ts
  • tests/unit/actions/active-sessions-monitoring.test.ts
  • tests/unit/actions/usage-logs-export-retry-count.test.ts
  • tests/unit/deploy-dockerfile-contract.test.ts
  • tests/unit/repository/activity-stream-replay.test.ts
  • tests/unit/repository/usage-logs-replay-projection.test.ts
  • tests/unit/repository/usage-logs-sessionid-filter.test.ts
  • tests/unit/repository/usage-logs-sessionid-suggestions.test.ts
🚧 Files skipped from review as they are similar to previous changes (15)
  • src/repository/_shared/usage-log-filters.ts
  • src/actions/usage-logs.ts
  • tests/unit/repository/usage-logs-replay-projection.test.ts
  • tests/unit/actions/usage-logs-export-retry-count.test.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
  • src/repository/activity-stream.ts
  • src/lib/api-client/v1/actions/usage-logs.ts
  • tests/unit/actions/active-sessions-monitoring.test.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/actions/active-sessions.ts
  • tests/unit/repository/usage-logs-sessionid-filter.test.ts
  • tests/unit/repository/activity-stream-replay.test.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx
  • src/repository/usage-logs.ts

Comment thread package.json
"private": true,
"engines": {
"node": ">=22.15.0"
"node": ">=22.19.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n --hidden --glob '!node_modules' \
  'setup-node|node-version|FROM .*node:|NODE_VERSION|22\.15|22\.18|22\.19' \
  .github deploy dev package.json

Repository: ding113/claude-code-hub

Length of output: 872


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== package.json engines =="
python3 - <<'PY'
import json
from pathlib import Path
p = Path("package.json")
data=json.loads(p.read_text())
print(data.get("engines"))
PY

echo "== Node references across tracked files =="
git ls-files | xargs rg -n --hidden --glob '!node_modules' \
  'setup-node|node-version|node:|NODE_VERSION|engines|22\.19|22\.18|22\.15|22\.20|node_version|\.nvmrc|package-lock.json' || true

echo "== Node versions advertised by Node Debian/Trixie tags =="
curl -fsSL https://deb.nodesource.com/node_22.x/dists/trixie/main/binary-amd64/Packages.gz \
  | gunzip -c \
  | perl -nle 'BEGIN{$/="\nPackage: "} END{print}' \
  | awk 'BEGIN{RS="Package: "; FS="\n"} $1~/^Node\.js/ {for(i=1;i<=NF;i++){if($i~/^Version: /){v=$i; sub(/^Version: /,"",v); print v}}}' \
  | sort -V | tail -5 || true

echo "== Dockerfile excerpts =="
sed -n '1,45p' deploy/Dockerfile
echo "---"
sed -n '1,45p' deploy/Dockerfile.dev || true

echo "== Workflow excerpts =="
sed -n '180,205p' .github/workflows/release.yml
echo "---"
sed -n '50,68p' .github/workflows/dev.yml
echo "---"
sed -n '25,38p' .github/workflows/pr-check.yml

Repository: ding113/claude-code-hub

Length of output: 45050


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== deploy/Dockerfile.node-version 相关上下文 =="
git ls-files deploy/Dockerfile Dockerfile | xargs -r -I{} sh -c 'echo "--- {}"; rg -n --hidden --glob "!node_modules" "setup-node|node-version|bun-version|node:22|FROM .*node:|NODE_VERSION|engines|formatting|dependencies" "{}" || true'

echo "== actions/setup-node 步骤上下文 =="
git ls-files .github/workflows pr-check.yml dev.yml release.yml | xargs -r -I{} sh -c 'echo "--- workflow {}"; sed -n "1,230p" "{}" | nl -ba | sed -n "20,210p"'

echo "== 非标准 Node 版本号引用位置 =="
rg -n --hidden --glob '!node_modules' \
  'node-version:\s*["'\'']*([^"'\''}]+)["'\'']|node-version:\s*20|FROM\s+node:22|Dockerfile\.node-version|setup-node' \
  .github Dockerfile deploy package.json | head -80

Repository: ding113/claude-code-hub

Length of output: 2511


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== root Dockerfile node:22-slim context =="
sed -n '1,35p' Dockerfile

echo "== package-lock scripts/engine hints =="
python3 - <<'PY'
import json
from pathlib import Path
p=Path("package-lock.json")
if p.exists():
    data=json.loads(p.read_text())
print(data.get("requires"), data.get("lockfileVersion"), data.get("engines"))
pkg=json.loads(Path("package.json").read_text())
print("package engines", pkg.get("engines"))
print("package scripts key count", len(pkg.get("scripts", {})))
PY

echo "== explicit local Node engine/runtime checks =="
rg -n --hidden --glob '!node_modules' \
  'process\.versions|node\.versions|check for|Node\.js >=|>=22\.19|engines\.node|must install|installing Node|nvm|volta|direnv|README\.|README\.en\.md' \
  .github Dockerfile docker-compose*.yml deploy scripts src README.md README.en.md package.json package-lock.json | head -120

Repository: ding113/claude-code-hub

Length of output: 1344


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path
for name in ["package.json", "package-lock.json"]:
    p = Path(name)
    if p.exists():
        data = json.loads(p.read_text())
        print(f"--- {name} ---")
        print("lockfileVersion:", data.get("lockfileVersion"))
        print("engines:", data.get("engines"))
else False
PY

echo "== scripts/engine notes =="
rg -n --hidden --glob '!node_modules' \
  'Node\.js >=|>=22\.19|>=22\.15|Node\.js|Node.js|setup-node|node-version|FROM node:22|from node:22|engines\.node|must install|nvm|volta' \
  .github Dockerfile deploy README.md README.en.md README* package.json scripts src/server.js | head -160

Repository: ding113/claude-code-hub

Length of output: 241


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path
for name in ["package.json", "package-lock.json"]:
    p = Path(name)
    if p.exists():
        data = json.loads(p.read_text())
        print(f"--- {name} ---")
        print("lockfileVersion:", data.get("lockfileVersion"))
        print("engines:", data.get("engines"))
PY

echo "== scripts/engine notes =="
rg -n --hidden --glob '!node_modules' \
  'Node\.js >=|>=22\.19|>=22\.15|Node\.js|Node.js|setup-node|node-version|FROM node:22|from node:22|engines\.node|must install|nvm|volta' \
  .github Dockerfile deploy README.md README.en.md README* package.json scripts src/server.js | head -160

Repository: ding113/claude-code-hub

Length of output: 3225


将 Node 版本声明与部署运行时保持一致。

package.json 要求 node >=22.19.0,但 deploy/Dockerfile.dev 仍使用 node:trixie-slim,且 Docker 镜像的 Node 运行时版本可能无法稳定满足 22.19.0。如果 node:trixie-slim 提供的版本低于该下限,生产/开发镜像的安装或启动会失败;如果无法保证高于下限,请将 engines.node 降至实际支持的版本,或在镜像中固定/检查 Node 版本。

🤖 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 `@package.json` at line 6, Align the Node runtime declared by the package.json
engines.node setting with the version used by deploy/Dockerfile.dev: either pin
the Docker base image to a Node release meeting >=22.19.0 or lower engines.node
to the runtime version actually supported, ensuring dependency installation and
startup remain compatible.

@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: e4d703044a

ℹ️ 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 src/repository/usage-logs.ts Outdated
? await loadUsageLogSourceSessionIdsByIdentity(
logs,
{ keyString },
{ message: messageRows.length > 0, ledger: ledgerRows.length > 0 }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Query both stores when hydrating grouped session IDs

When the read-only/my-usage page produces a page from only one backing query—for example, a date or endpoint filter returns current message_request rows while older rows for the same prefix survive only in usage_ledger—these booleans disable hydration from the other store. Because the hydrator otherwise ignores page filters and is intended to return all physical IDs grouped under the public identity, the tooltip silently omits IDs based solely on which source contributed rows to this page; consult both stores within the key scope rather than using the page-query row counts as availability flags.

Useful? React with 👍 / 👎.

Comment thread src/repository/activity-stream.ts Outdated
cacheCreationInputTokens: messageRequest.cacheCreationInputTokens,
cacheReadInputTokens: messageRequest.cacheReadInputTokens,
rowNum: sql<number>`ROW_NUMBER() OVER (PARTITION BY ${messageRequest.sessionId} ORDER BY ${messageRequest.createdAt} DESC)`,
rowNum: sql<number>`ROW_NUMBER() OVER (PARTITION BY ${messageSessionIdentity} ORDER BY ${messageRequest.createdAt} DESC)`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Limit after selecting one row per canonical session

When one prefix-affinity identity contributes more than limit * 2 of the newest requests, this outer limit is applied before the JavaScript rowNum === 1 filter, so every fetched row can belong to that single canonical partition and only one survives. Collapsing multiple physical IDs into messageSessionIdentity makes this regression especially likely for prefix sessions; other observed active sessions can then be displaced by the generic fallback instead of receiving their latest row. Filter the windowed result to rowNum = 1 in a subquery, or use an equivalent per-identity query, before applying the limit.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/unit/repository/usage-logs-sessionid-filter.test.ts (1)

61-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

将源代码文本断言改为运行时行为断言。

readFileSync(...).toContain(...) 只证明源码中存在指定文本。它没有执行只读 key hydration,也没有验证空页场景下 messageledger 两个数据源都会查询。请使用现有数据库 mock 调用实际路径,并断言两个查询的调用和结果。

🤖 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 `@tests/unit/repository/usage-logs-sessionid-filter.test.ts` around lines 61 -
64, Replace the source-text assertion in the readonly key hydration test with a
runtime test that uses the existing database mocks to execute the hydration path
with one backing-store page empty. Assert both message and ledger queries are
invoked and verify the combined hydration result, using the relevant usage-logs
repository symbols rather than readFileSync.
🤖 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 `@src/repository/activity-stream.ts`:
- Around line 107-110: 在 src/repository/activity-stream.ts 的查询构建流程中,使用
messageSessionIdentity 对 ROW_NUMBER() 结果应用 rowNum = 1 的数据库级过滤,再按 createdAt
降序排序并应用结果限制后返回应用层。同步更新 tests/unit/repository/activity-stream-replay.test.ts
第129-144行,断言规范会话及最新请求 ID,并移除对 boundary.limits 为空的断言。

---

Nitpick comments:
In `@tests/unit/repository/usage-logs-sessionid-filter.test.ts`:
- Around line 61-64: Replace the source-text assertion in the readonly key
hydration test with a runtime test that uses the existing database mocks to
execute the hydration path with one backing-store page empty. Assert both
message and ledger queries are invoked and verify the combined hydration result,
using the relevant usage-logs repository symbols rather than readFileSync.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 79f1bcd9-44e4-41e9-b9cb-f053d4f2d97b

📥 Commits

Reviewing files that changed from the base of the PR and between e4d7030 and 4d27c51.

📒 Files selected for processing (4)
  • src/repository/activity-stream.ts
  • src/repository/usage-logs.ts
  • tests/unit/repository/activity-stream-replay.test.ts
  • tests/unit/repository/usage-logs-sessionid-filter.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/repository/usage-logs.ts

Comment thread src/repository/activity-stream.ts Outdated

@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: 4d27c510ad

ℹ️ 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 src/repository/activity-stream.ts Outdated
cacheCreationInputTokens: messageRequest.cacheCreationInputTokens,
cacheReadInputTokens: messageRequest.cacheReadInputTokens,
rowNum: sql<number>`ROW_NUMBER() OVER (PARTITION BY ${messageRequest.sessionId} ORDER BY ${messageRequest.createdAt} DESC)`,
rowNum: sql<number>`ROW_NUMBER() OVER (PARTITION BY ${messageSessionIdentity} ORDER BY ${messageRequest.createdAt} DESC)`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Filter window rows in SQL before returning them

When an observed prefix-affinity identity has accumulated many requests, this query returns every matching historical row to Node and only then keeps rowNum === 1; because the activity stream is polled, a long-lived identity can repeatedly transfer and materialize an unbounded result set just to display one entry. The current revision removed the former limit but still performs the window filter in JavaScript, so wrap the window query and apply rowNum = 1 in SQL before limiting the final identities.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@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: e1c08e394e

ℹ️ 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 on lines +109 to +110
.orderBy(messageSessionIdentity, desc(messageRequest.createdAt))
.limit(limit);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Order deduplicated active sessions by recency

When more observed active identities have database rows than limit, this current DISTINCT ON revision orders the result by canonical identity before applying the limit, so it selects the lexicographically first identities rather than those with the newest requests. The later in-memory sort cannot recover identities already discarded, causing recent active sessions to disappear from the activity stream; select one row per identity in a subquery, then order those rows by creation time before limiting.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@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: df489f211d

ℹ️ 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 on lines +127 to +130
const conditions = [inArray(ledgerSessionIdentity, sessionIds)];
if (scope.userId !== undefined) conditions.push(eq(usageLedger.userId, scope.userId));
if (scope.keyId !== undefined) conditions.push(eq(keysTable.id, scope.keyId));
if (scope.keyString !== undefined) conditions.push(eq(usageLedger.key, scope.keyString));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep ledger source-ID hydration on an indexed path

When the admin dashboard falls back to ledger-only storage without a user/key scope, every usage-log page now runs this identity aggregate across usage_ledger. The only index beginning with COALESCE(session_identity, session_id) is the partial idx_usage_ledger_session_identity_created_at in src/drizzle/schema.ts, whose predicate requires blocked_by IS NULL AND is_replay = false; because this query implies neither predicate, PostgreSQL cannot use that index and must scan/group the ledger on each paginated fetch. Add an applicable identity index or otherwise make the hydration query use an indexed lookup while preserving the required replay/blocked rows.

Useful? React with 👍 / 👎.

Comment on lines +145 to +148
sql`(
COALESCE(${messageRequest.sessionIdentity}, ${messageRequest.sessionId}) = ${trimmedSessionId}
OR ${messageRequest.sessionId} = ${trimmedSessionId}
)`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the reserved session-identity namespace

When a client-controlled physical ID equals another session's canonical pfx: identity, filtering by that canonical identity now also returns the unrelated physical session through this raw-ID alternative. buildPublicSessionIdentity in src/lib/request-identity.ts deliberately maps client-provided pfx: and sid: values into a key-bound sid: namespace to prevent this alias, but both this condition and the new ledger equivalent bypass that isolation. Fresh evidence beyond the earlier activity-stream issue is that this revision introduces the same alternative in the usage-log filters, so canonical and physical searches need an explicit discriminator rather than an ambiguous OR.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@ding113
ding113 merged commit 7b628bb into dev Aug 2, 2026
10 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Claude Code Hub Roadmap Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR (< 500 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant