Skip to content

feat(logs): show live upstream providers - #1379

Merged
ding113 merged 1 commit into
devfrom
provider-racing-stacking-reasoning-ui
Aug 2, 2026
Merged

feat(logs): show live upstream providers#1379
ding113 merged 1 commit into
devfrom
provider-racing-stacking-reasoning-ui

Conversation

@ding113

@ding113 ding113 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Persist active upstream providers in the existing Redis live usage snapshot.
  • Reconstruct Discovery racing providers from routing lifecycle events and track legacy hedge/fallback activity.
  • Stack live racing providers with a full Tooltip list; switch to the active fallback provider in real time.
  • Allow reasoning-effort override badges to overflow into the next column without changing column widths.

Verification

  • bun run typecheck
  • bun run lint
  • bun run build (passes; existing Edge/Node compatibility warnings remain)
  • Focused Vitest: 69 passed
  • Full Vitest completed 8,189 tests before the final test-only assertion adjustment; final full rerun was interrupted with exit 143 after an unrelated provider action test timed out under suite contention
  • The timed-out provider action test passed independently in 2.6s
  • git diff --check

Greptile Summary

This PR adds live upstream-provider snapshots to Redis and renders active racing or fallback providers in the virtualized logs table.

  • Tracks legacy hedge participants through attempt launch and cleanup.
  • Reconstructs Discovery participants by replaying routing lifecycle events.
  • Adds a stacked provider display with a complete tooltip list.
  • Allows reasoning-effort badges to overflow their fixed-width column.

Confidence Score: 4/5

The Discovery lifecycle gap should be fixed before merging because live logs can identify a failed sticky provider as still connected during the replacement race.

The new event replay assumes every started Discovery attempt receives an immediate terminal event, but two reachable sticky fallback branches continue routing after removing an attempt without recording its completion.

Files Needing Attention: src/lib/redis/live-chain-store.ts and the Discovery cleanup branches in src/app/v1/_lib/proxy/forwarder.ts

Important Files Changed

Filename Overview
src/lib/redis/live-chain-store.ts Adds active-provider snapshot storage and Discovery event replay, but unmatched sticky-probe start events can temporarily retain disconnected providers.
src/app/v1/_lib/proxy/forwarder.ts Adds legacy hedge provider lifecycle updates; those paths are balanced, while existing Discovery sticky fallback behavior exposes a gap in the new replay model.
src/app/v1/_lib/proxy/session.ts Adds ref-counted active-provider state and includes it in serialized live-chain snapshots.
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx Renders up to three active providers as a stack and exposes the complete list through a tooltip.
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx Allows the fixed-width reasoning-effort cell to visibly overflow without changing its width.
src/repository/usage-logs.ts Extends the live-chain row contract with the optional active-provider list.

Sequence Diagram

sequenceDiagram
  participant F as ProxyForwarder
  participant S as ProxySession
  participant R as Redis live snapshot
  participant U as Usage Logs UI
  F->>S: attempt_started / active-provider update
  S->>R: persist chain, trace, and active providers
  F->>S: attempt_finished / winner_committed
  S->>R: persist updated snapshot
  U->>R: poll live request state
  R-->>U: activeProviders
  U-->>U: render provider stack and tooltip
Loading
Prompt To Fix All With AI
### Issue 1
src/lib/redis/live-chain-store.ts:92-99
**Unmatched attempts remain active**

When a Discovery sticky probe fails or enters sticky-timeout fallback, the forwarder removes the attempt without immediately emitting `attempt_finished`; this replay therefore retains the disconnected provider until the request settles or commits a winner, causing live logs to show it alongside the replacement round.

---

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

Reviews (1): Last reviewed commit: "feat(logs): show live upstream providers" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used (3)

@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

📝 Walkthrough

Walkthrough

PR 新增实时活跃供应商跟踪和快照持久化。日志表显示当前活跃 provider 堆叠,并支持 fallback 切换。思考强度内容改为允许溢出并提升堆叠层级。相关测试已更新。

Changes

实时活跃供应商展示

Layer / File(s) Summary
活跃供应商快照与推导
src/lib/redis/live-chain-store.ts, src/lib/redis/live-chain-store.storage.test.ts, src/repository/usage-logs.ts
新增 LiveProviderSnapshotactiveProviders 字段。快照支持从 Discovery 路由事件及 legacy provider chain 推导活跃 provider。
代理层 provider 生命周期跟踪
src/app/v1/_lib/proxy/session.ts, src/app/v1/_lib/proxy/forwarder.ts
ProxySession 使用引用计数维护活跃 provider。Hedge 尝试启动、取消或失败时更新该状态,并写入实时链路快照。
日志表 provider 堆叠展示
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx, src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx, src/app/[locale]/dashboard/logs/_components/thinking-effort-display.tsx
新增 LiveProviderStack。日志表优先显示 activeProviders,并允许 provider 与思考强度内容溢出显示。
状态与布局测试
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.test.tsx, src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx, src/app/[locale]/dashboard/logs/_components/thinking-effort-display.test.tsx
新增 racing 和 fallback 切换测试,并更新溢出及 relative z-20 样式断言。

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

Possibly related PRs

Suggested reviewers: brisbanehuang

🚥 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 标题准确概括了日志中展示实时上游供应商这一主要变更。
Description check ✅ Passed 描述涵盖了实时供应商持久化、展示、fallback 切换及样式调整,与变更内容一致。
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch provider-racing-stacking-reasoning-ui

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 on lines +92 to +99
if (event.type === "attempt_started" && event.attemptId && event.provider) {
sawAttemptLifecycle = true;
activeAttempts.set(event.attemptId, {
id: event.provider.id,
name: event.provider.name ?? String(event.provider.id),
});
continue;
}

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 Unmatched attempts remain active

When a Discovery sticky probe fails or enters sticky-timeout fallback, the forwarder removes the attempt without immediately emitting attempt_finished; this replay therefore retains the disconnected provider until the request settles or commits a winner, causing live logs to show it alongside the replacement round.

Knowledge Base Used: Proxy request pipeline

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/redis/live-chain-store.ts
Line: 92-99

Comment:
**Unmatched attempts remain active**

When a Discovery sticky probe fails or enters sticky-timeout fallback, the forwarder removes the attempt without immediately emitting `attempt_finished`; this replay therefore retains the disconnected provider until the request settles or commits a winner, causing live logs to show it alongside the replacement round.

**Knowledge Base Used:** [Proxy request pipeline](https://app.greptile.com/ygxz/-/custom-context/knowledge-base/ding113/claude-code-hub/-/docs/proxy-pipeline.md)

---

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

@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: 69f164feb5

ℹ️ 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 +435 to +436
this.liveActiveProviders.clear();
this.liveActiveProviderCounts.clear();

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 Avoid sharing live-provider maps with hedge shadows

When a second streaming-hedge attempt is created, createStreamingShadowSession shallow-copies the original ProxySession with Object.assign, so the shadow and original share these Map instances. The shadow's existing setProvider(provider) call now executes these clear() operations and removes the still-connected primary from the original session's live state; consequently, during an actual multi-provider race, the dashboard shows only the newly launched provider instead of all connected providers. Clone the maps for shadow sessions or restrict live-provider mutations to the owning session.

Useful? React with 👍 / 👎.

};

attempts.add(attempt);
session.addLiveActiveProvider(provider);

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 Register the initial hedge provider only once

For the initial streaming-hedge attempt, attemptSession is the original session, and the immediately preceding attemptSession.setProvider(provider) already records the provider with count 1. This additional registration raises its count to 2, while a normal attempt failure calls removeLiveActiveProvider only once, leaving the failed provider displayed as active while fallback selection and related failure handling run. Register launches only when setProvider did not already register that attempt, or remove the count-based duplicate.

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: 2

🧹 Nitpick comments (1)
src/lib/redis/live-chain-store.ts (1)

129-159: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

deriveLegacyActiveProviders 未覆盖全部终止性 reason,且与生产环境的引用计数逻辑脱节。

deriveLegacyActiveProvidersswitch 语句只处理部分 reason 值。client_error_non_retryableendpoint_pool_exhaustedvendor_type_all_timeout 等终止性失败原因未列入 delete 分支,一旦命中这些原因,该 provider 会一直留在返回的 active 列表中。

当前生产代码路径(session.tsflushLiveObservability)始终显式传入 activeProviders 参数,因此该函数目前只在测试中被调用。但作为 writeLiveChain 的默认参数,它与 ProxySession 的引用计数机制(liveActiveProviders/liveActiveProviderCounts)语义不一致。如果未来有代码调用 writeLiveChain 时省略第四个参数,会得到与真实会话状态不一致的结果。

建议为该函数补全终止性 reasondelete 分支,或添加注释说明它仅作为兜底/测试路径使用,避免未来误用。

🤖 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/lib/redis/live-chain-store.ts` around lines 129 - 159, Update
deriveLegacyActiveProviders so every terminal failure reason, including
client_error_non_retryable, endpoint_pool_exhausted, and
vendor_type_all_timeout, removes the provider from activeProviders. Align its
switch behavior with the production liveActiveProviders/liveActiveProviderCounts
semantics while preserving existing success and winner handling.
🤖 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/virtualized-logs-table.tsx:
- Around line 105-127: Update the TooltipTrigger child in the live provider
stack to a native focusable button with type="button", preserving the existing
provider badges and tooltip behavior. Add a translated aria-label using the
component’s next-intl translation mechanism so keyboard users can identify and
activate it, and ensure the focused trigger reveals the full provider list.

In `@src/app/v1/_lib/proxy/session.ts`:
- Around line 210-211: 修复 active provider 状态的生命周期管理:在
src/app/v1/_lib/proxy/session.ts 的 210-211 行,将活动 attempt 映射设计为仅由根 session
持有和管理的独立状态;在 session.ts 429-467 行拆分 setProvider 的 provider 选择与 attempt 引用计数职责,并在
907-919 行只持久化根 session 管理的活动 attempt 快照。更新 src/app/v1/_lib/proxy/forwarder.ts
4561 行仅注销根 session 注册的 attempt,在 4859-4861 行统一失败与取消的幂等注销路径,并在 5351 行确保每个 Hedge
attempt 只注册一次且 shadow session 不重置根 session 的活动列表。

---

Nitpick comments:
In `@src/lib/redis/live-chain-store.ts`:
- Around line 129-159: Update deriveLegacyActiveProviders so every terminal
failure reason, including client_error_non_retryable, endpoint_pool_exhausted,
and vendor_type_all_timeout, removes the provider from activeProviders. Align
its switch behavior with the production
liveActiveProviders/liveActiveProviderCounts semantics while preserving existing
success and winner handling.
🪄 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: 1c5d0668-5241-497f-8f31-c4b05741ae4b

📥 Commits

Reviewing files that changed from the base of the PR and between 519c605 and 69f164f.

📒 Files selected for processing (11)
  • src/app/[locale]/dashboard/logs/_components/thinking-effort-display.test.tsx
  • src/app/[locale]/dashboard/logs/_components/thinking-effort-display.tsx
  • 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/v1/_lib/proxy/forwarder.ts
  • src/app/v1/_lib/proxy/session.ts
  • src/lib/redis/live-chain-store.storage.test.ts
  • src/lib/redis/live-chain-store.ts
  • src/repository/usage-logs.ts

Comment on lines +105 to +127
<TooltipProvider>
<Tooltip delayDuration={250}>
<TooltipTrigger asChild>
<span
className="flex min-w-0 items-center -space-x-2 cursor-help"
data-slot="live-provider-stack"
>
{visibleProviders.map((provider) => (
<span
key={provider.id}
className="relative max-w-[68px] truncate rounded-md border bg-background px-1.5 py-0.5 text-xs text-foreground shadow-sm"
>
{provider.name}
</span>
))}
{hiddenProviderCount > 0 && (
<span className="relative rounded-md border bg-muted px-1.5 py-0.5 text-xs text-muted-foreground shadow-sm">
+{hiddenProviderCount}
</span>
)}
</span>
</TooltipTrigger>
<TooltipContent className="max-w-[320px]" side="bottom" align="start">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
git ls-files | rg 'src/app/.*/dashboard/logs/_components/virtualized-logs-table\.tsx|package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock' || true

echo "== relevant source =="
if [ -f src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx ]; then
  wc -l src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  sed -n '1,190p' 'src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx'
fi

echo "== search LiveProviderStack/usages =="
rg -n "LiveProviderStack|visibleProviders|hiddenProviderCount|TooltipTrigger" src || true

echo "== tooltip imports/usages in file =="
rg -n "from ['\"]`@radix-ui/react-tooltip`['\"]|TooltipTrigger|TooltipContent" src/app || true

echo "== dependencies =="
if [ -f package.json ]; then
  node -e "const p=require('./package.json'); console.log(JSON.stringify({deps:p.dependencies, devDeps:p.devDependencies}, null, 2))"
fi

Repository: ding113/claude-code-hub

Length of output: 2038


🌐 Web query:

Radix UI TooltipTrigger asChild non-focusable element span keyboard accessibility button type button

💡 Result:

When using Radix UI TooltipTrigger with the asChild prop, the child element must be a focusable element (such as a button, anchor, or input) to maintain accessibility [1][2]. If you replace the default trigger with a non-focusable element like a span or div, you break the component's keyboard accessibility [1][2]. Specifically: 1. Keyboard users will be unable to focus the trigger to activate the tooltip [2]. 2. Screen readers may not correctly identify the element as an interactive trigger, and accessibility attributes like aria-describedby may not be attached or announced as expected [3][2]. 3. Radix UI documentation explicitly states that when using asChild, it is the developer's responsibility to ensure the resulting element remains accessible and functional, which requires it to be able to respond to pointer and keyboard events [1]. If you find yourself needing to wrap a non-focusable element (like text in a span) to avoid nested button issues (e.g., placing a tooltip inside another button), simply changing it to a span will cause a loss of keyboard functionality [3][4]. To maintain accessibility in such cases, you must manually ensure the element is focusable and interactive, for example by adding tabIndex={0} and appropriate keyboard event listeners to the span, although this is generally discouraged in favor of using natively focusable elements like buttons or links [5][2]. When using asChild with a properly focusable element (like your own button component), ensure your component correctly forwards refs and spreads props so that Radix can attach the necessary aria attributes and event handlers [1][6][2].

Citations:


使完整 provider 列表可通过键盘访问。

TooltipTrigger asChild 下的 <span> 不是焦点元素,键盘用户无法聚焦或激活 Tooltip。将触发器改为原生的可聚焦元素,例如 <button type="button">,并使用 next-intl 提供翻译后的 aria-label。验证 Tab 聚焦触发器后可以显示完整 provider 列表。

🤖 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 105 - 127, Update the TooltipTrigger child in the live provider
stack to a native focusable button with type="button", preserving the existing
provider badges and tooltip behavior. Add a translated aria-label using the
component’s next-intl translation mechanism so keyboard users can identify and
activate it, and ensure the focused trigger reveals the full provider list.

Source: Coding guidelines

Comment on lines +210 to +211
private liveActiveProviders = new Map<number, LiveProviderSnapshot>();
private liveActiveProviderCounts = new Map<number, number>();

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 | 🟠 Major | 🏗️ Heavy lift

修复 active provider 状态的生命周期模型。 provider 选择和运行 attempt 共用同一个引用计数。初始 Hedge attempt 被重复计数。shadow session 会重置根 session 的共享映射。

  • src/app/v1/_lib/proxy/session.ts#L210-L211: 将活动 attempt 状态设计为仅由根 session 管理的独立状态。
  • src/app/v1/_lib/proxy/session.ts#L429-L467: 不要让 setProvider 同时承担 provider 选择和 attempt 引用计数。
  • src/app/v1/_lib/proxy/session.ts#L907-L919: 只持久化经过根 session 生命周期管理的活动 attempt 快照。
  • src/app/v1/_lib/proxy/forwarder.ts#L4561-L4561: 只注销此前由根 session 注册的 attempt。
  • src/app/v1/_lib/proxy/forwarder.ts#L4859-L4861: 使失败清理与取消清理共享幂等的注销路径。
  • src/app/v1/_lib/proxy/forwarder.ts#L5351-L5351: 每个 Hedge attempt 只能注册一次,且 shadow session 不得重置根 session 的活动列表。
📍 Affects 2 files
  • src/app/v1/_lib/proxy/session.ts#L210-L211 (this comment)
  • src/app/v1/_lib/proxy/session.ts#L429-L467
  • src/app/v1/_lib/proxy/session.ts#L907-L919
  • src/app/v1/_lib/proxy/forwarder.ts#L4561-L4561
  • src/app/v1/_lib/proxy/forwarder.ts#L4859-L4861
  • src/app/v1/_lib/proxy/forwarder.ts#L5351-L5351
🤖 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/v1/_lib/proxy/session.ts` around lines 210 - 211, 修复 active provider
状态的生命周期管理:在 src/app/v1/_lib/proxy/session.ts 的 210-211 行,将活动 attempt 映射设计为仅由根
session 持有和管理的独立状态;在 session.ts 429-467 行拆分 setProvider 的 provider 选择与 attempt
引用计数职责,并在 907-919 行只持久化根 session 管理的活动 attempt 快照。更新
src/app/v1/_lib/proxy/forwarder.ts 4561 行仅注销根 session 注册的 attempt,在 4859-4861
行统一失败与取消的幂等注销路径,并在 5351 行确保每个 Hedge attempt 只注册一次且 shadow session 不重置根 session
的活动列表。

};

attempts.add(attempt);
session.addLiveActiveProvider(provider);

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] Initial hedge participant stays in the live provider stack after it fails

Why this is a problem: startAttempt(initialProvider, true) already routes through attemptSession.setProvider(provider), and the new setProvider() bookkeeping seeds liveActiveProviderCounts with 1. Adding the same provider again here bumps the initial participant to 2. When that first attempt later fails, handleAttemptFailure() / abortAttempt() only removes one count, so the failed provider remains in _liveChain.activeProviders and the logs UI continues to show it as an active upstream even though the connection is already gone.

Suggested fix:

attempts.add(attempt);
if (!useOriginalSession) {
  session.addLiveActiveProvider(provider);
}

@github-actions github-actions Bot added the size/M Medium PR (< 500 lines) label Aug 1, 2026

@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

One hedge-path state-tracking regression needs attention before merge. The new live-provider plumbing is localized, but the initial participant bookkeeping can leave a failed upstream displayed as still active in the logs UI.

PR Size: M

  • Lines changed: 341
  • Files changed: 11

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 1 0 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)

  • src/app/v1/_lib/proxy/forwarder.ts:5351session.addLiveActiveProvider(provider) double-counts the initial hedge participant because setProvider() has already seeded the same provider on the original session. When that first attempt fails, only one count is removed, so _liveChain.activeProviders can still show a dead upstream as active.

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 #1379, applied the size/M label, and posted the required summary review on GitHub.
  • Filed 1 high-priority inline comment at src/app/v1/_lib/proxy/forwarder.ts:5351.
  • Issue: the new live-provider bookkeeping double-counts the initial hedge participant, so when that first attempt fails it can remain in _liveChain.activeProviders and still appear active in the logs UI.
  • Suggested fix in the review: only call session.addLiveActiveProvider(provider) for non-original/shadow hedge attempts, or otherwise avoid seeding the same provider twice.
  • No other changed-line issues survived the full-context validation threshold.
  • bunx is not installed in this runner, so test execution was not available from this environment.

@ding113
ding113 merged commit 1b7775b into dev Aug 2, 2026
23 of 24 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