Skip to content

feat(discovery): merge bounded Discovery stack#1359

Merged
ding113 merged 115 commits into
devfrom
integration/discovery-stack-20260723
Jul 23, 2026
Merged

feat(discovery): merge bounded Discovery stack#1359
ding113 merged 115 commits into
devfrom
integration/discovery-stack-20260723

Conversation

@ding113

@ding113 ding113 commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

This PR is the final integration of the bounded Discovery stack into dev.

It includes:

The five stacked PRs were retargeted to and merged into integration/discovery-stack-20260723. Merge conflicts were resolved on that branch while preserving both Discovery trace and Codex reasoning-effort behavior.

Migration order is preserved as:

0109_nice_shriek
0110_daffy_rawhide_kid
0111_happy_mauler

#1338 is intentionally not included.

Validation

  • bun run test: 7504 passed, 13 skipped, 0 failed
  • bun run typecheck
  • bun run validate:migrations: 113 migrations passed
  • bun run lint: exit 0, existing warnings only
  • bun run build: Next.js 16.2.11, 187/187 static pages generated
  • git diff --check

The integration worktree is clean at 104e42d67819302d98f093f9633c12a41eb76cd5.

Greptile Summary

This PR integrates the complete bounded Discovery stack: a new provider-racing system that runs multiple concurrent upstream attempts within configurable SLA windows, validates stream prefixes for protocol correctness before committing a winner, and manages per-session sticky bindings through versioned Redis operations with atomic Lua CAS scripts.

  • Discovery coordinator + validity parser (discovery-coordinator.ts, discovery-validity.ts): pure state machine for round/SLA lifecycle and per-chunk SSE parsing across Anthropic, OpenAI, and Gemini protocols; both are dependency-free and unit-tested.
  • Versioned session binding (session-binding.ts, lua-scripts.ts): eight new Lua scripts implement compare-and-set, touch, clear (with cooldown), terminate, and discovery-lease operations; rolling-upgrade reconciliation between the new canonical hash and the legacy session:*:provider mirror is handled atomically in READ_OR_RECONCILE_SESSION_BINDING.
  • Routing trace + durable outbox (routing-trace-outbox.ts, routing-trace-persistence.ts, migration 0111): the routing_trace JSONB column is added to message_request; post-terminal traces are staged in a Redis hash outbox and replayed by a background scheduler so a shutdown-time DB outage cannot lose trace data; the ledger trigger is narrowed to exclude the new column.
  • Schema (0110, 0111): seven new system_settings columns add Discovery tunables (disabled by default); migration order is preserved and validated.

Confidence Score: 5/5

Safe to merge. Discovery is disabled by default (discovery_enabled = false in the new settings columns), the outbox/binding operations degrade gracefully when Redis is unavailable, and the ledger trigger is correctly narrowed to exclude the new routing-trace column.

No correctness issues found across the coordinator state machine, Lua binding scripts, outbox replay, or stream validity parser. The five constituent PRs represent a large but cohesive change; the 7504-test suite provides broad coverage of the new paths, and every resource-lifecycle edge case (lease renewal, loser cancellation, setup-reservation cleanup) has explicit guards. The only outstanding concern from prior review rounds (doubled Redis reads in readLiveChainBatch) is a non-blocking performance note.

No files require special attention. The new forwarder.ts orchestration (~2900 added lines) is the densest area, but its structure is clearly factored and covered by dedicated unit tests.

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/discovery-coordinator.ts New pure state machine for the bounded Discovery round lifecycle; no network or timer dependencies. Logic is complex but consistently guarded by epoch checks and well-covered by the unit test suite.
src/app/v1/_lib/proxy/discovery-validity.ts New stateful SSE/JSON stream parser for Discovery prefix validation. Byte and event limits are enforced before readiness is established; once ready, parsing continues without limits.
src/app/v1/_lib/proxy/forwarder.ts Major addition (~2900 lines): orchestrates the full Discovery attempt lifecycle—rollout bucketing, lease acquisition, coordinator-driven round/SLA timers, loser cancellation, binding CAS, and deferred finalization. Very dense but structurally sound.
src/app/v1/_lib/proxy/response-handler.ts Adds Discovery lease renewal heartbeat, binding touch, winner finalization, and hedge-loser billing for the new mode. Lease lifecycle is correctly fenced behind ensureOwned before any binding mutation.
src/lib/redis/session-binding.ts New 1539-line module implementing versioned session binding with a capability probe, rolling-upgrade reconciliation, CAS/clear/touch/terminate operations, cooldown markers, and Discovery lease management. All multi-key operations are delegated to atomic Lua scripts.
src/lib/redis/lua-scripts.ts Eight new Lua scripts for atomic binding operations. All correctly validate inputs, check key ownership, and maintain canonical/legacy mirror consistency. Single-key fallback scripts are provided for Redis Cluster where multi-key scripts cannot be used.
src/repository/routing-trace-outbox.ts New Redis hash outbox for durable post-terminal routing trace persistence. Stage-if-not-older and delete-if-unchanged Lua scripts ensure monotonic updates and idempotent replays; a background scheduler replays retained entries after restarts.
src/types/routing-trace.ts New routing trace type definitions with a thorough normalizeRoutingTrace deserializer that treats persisted JSON as untrusted and whitelist-validates every field.
drizzle/0111_happy_mauler.sql Adds nullable routing_trace JSONB column and correctly narrows the trg_upsert_usage_ledger trigger's UPDATE OF column list to exclude routing_trace, preventing trace-only patches from triggering unnecessary ledger recomputes.
src/lib/redis/live-chain-store.ts Adds a parallel routingTraceStore keyed identically to the existing chain store. Read paths now issue 2 Redis reads per session (noted as a P2 concern in a previous review). The deleteLiveChain cleanup correctly removes both keys.
src/repository/message.ts Adds updateMessageRequestRoutingTrace with outbox staging + monotonic DB persistence fallback. Both sync and async write modes are covered. Outbox receipt is acknowledged only after successful DB write, ensuring at-least-once delivery.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Forwarder as ProxyForwarder
  participant Coord as DiscoveryCoordinator
  participant Redis as Redis(Binding+Lease)
  participant Upstream as UpstreamProviders(N)
  participant Parser as DiscoveryValidityParser
  participant DB as PostgreSQL

  Client->>Forwarder: "POST /v1/messages (stream=true)"
  Forwarder->>Redis: readOrReconcile binding snapshot
  Forwarder->>Redis: acquireSessionDiscoveryLease
  Forwarder->>Coord: startStickyProbe / startDiscoveryRacing
  loop Each round (max maxRounds)
    Forwarder->>Upstream: Launch concurrent attempts (concurrency N)
    Upstream-->>Parser: SSE chunks
    Parser-->>Forwarder: "DiscoveryValidity {ready, terminal, error}"
    alt Attempt ready (valid prefix)
      Forwarder->>Coord: markReady(attemptId)
      Coord-->>Forwarder: commit_normal / none (priority gate)
    else Round SLA expires
      Forwarder->>Coord: onRoundBoundary
      Coord-->>Forwarder: promote_fallback / launch next round / cancel
    end
    alt Winner committed
      Forwarder->>Forwarder: cancelLosers + cancelSetupReservations
      Forwarder->>Redis: CAS session binding (sticky)
      Forwarder-->>Client: stream winner response
    end
  end
  alt All rounds exhausted
    Coord-->>Forwarder: terminal_failure / promote_fallback
    Forwarder-->>Client: stream fallback or error
  end
  Forwarder->>Redis: releaseDiscoveryLease
  Forwarder->>DB: updateMessageRequestRoutingTrace (outbox-backed)
Loading

Reviews (2): Last reviewed commit: "fix(discovery): address final integratio..." | Re-trigger Greptile

ding113 and others added 11 commits July 23, 2026 08:08
feat(proxy): add bounded streaming discovery
…0723' into agent/discovery-1349-on-integration

# Conflicts:
#	drizzle/meta/_journal.json
#	src/app/v1/_lib/proxy/forwarder.ts
#	src/app/v1/_lib/proxy/response-handler.ts
#	tests/unit/proxy/discovery-validity.test.ts
#	tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts
The reactive rectifier call in the hedge wave failure handler omitted the
required `error` argument, causing a TS2741 type error that broke CI.
Supply lastError, matching the two sibling call sites.

CI Run: https://github.com/ding113/claude-code-hub/actions/runs/29968232269

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(settings): configure bounded streaming discovery
…0723' into agent/discovery-1351-on-integration

# Conflicts:
#	messages/en/dashboard.json
#	messages/ja/dashboard.json
#	messages/ru/dashboard.json
#	messages/zh-CN/dashboard.json
#	messages/zh-TW/dashboard.json
#	src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
#	src/app/v1/_lib/proxy/forwarder.ts
#	tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts
…0723' into agent/discovery-1353-on-integration

# Conflicts:
#	messages/en/dashboard.json
#	messages/ja/dashboard.json
#	messages/ru/dashboard.json
#	messages/zh-CN/dashboard.json
#	messages/zh-TW/dashboard.json
#	src/app/[locale]/dashboard/logs/_components/usage-logs-table.test.tsx
#	src/app/v1/_lib/proxy/forwarder.ts
#	tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts
…race

feat(observability): add request-level Discovery routing trace
…0723' into agent/discovery-1353-on-integration
…cue-billing

fix(discovery): preserve final rescue and bill ready losers
@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 Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

新增有界流式 Discovery 路由、版本化会话绑定与 Redis 租约机制,并将 routing trace 贯通请求处理、持久化、outbox 回放及 Dashboard 日志展示。同时加入配置校验、数据库迁移、错误码、本地化文案和测试覆盖。

Changes

Bounded Discovery and routing trace

Layer / File(s) Summary
Discovery contracts and settings
src/types/*, src/lib/validation/*, src/lib/api/v1/schemas/*, src/drizzle/*, src/repository/system-config.ts
新增 Discovery 配置、路由追踪类型、数据库字段、默认值、窗口约束和结构化校验错误码。
Discovery routing lifecycle
src/app/v1/_lib/proxy/discovery-*, src/app/v1/_lib/proxy/forwarder.ts, src/app/v1/_lib/proxy/response-handler.ts
实现候选选择、轮次协调、协议有效性判定、fallback/rescue、失败者计费、租约释放及流式最终化。
Versioned session bindings
src/lib/redis/lua-scripts.ts, src/lib/redis/session-binding.ts, src/lib/session-manager.ts, src/lib/session-tracker.ts
新增版本化绑定、CAS、租约、冷却、租户隔离及 legacy fail-closed 回退语义。
Routing trace persistence
src/app/v1/_lib/proxy/session.ts, src/repository/message.ts, src/repository/message-write-buffer.ts, src/repository/routing-trace-*, src/lib/redis/live-chain-store.ts
记录并归一化 routing trace,支持 live-chain 独立存储、单调更新、post-terminal durable 写入和 Redis outbox 回放。
Dashboard rendering and localization
src/app/[locale]/dashboard/logs/_components/*, messages/*
在请求详情、Provider 链 Popover 和倍率徽标中展示 Discovery 路由、尝试、结果、绑定及计费信息,并补充多语言文案。
Validation and lifecycle tests
tests/unit/*, tests/integration/*, tests/api/*, package.json, tests/configs/*
新增协调器、协议解析、绑定、outbox、配置校验、日志 UI、流最终化和 Redis 集成测试。

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.83% 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 标题简洁且准确概括了本次合并有界 Discovery 栈的主要变更。
Description check ✅ Passed 描述与变更集一致,清楚说明了合并内容、迁移顺序和验证结果。
✨ 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 integration/discovery-stack-20260723

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.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

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

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

Comment on lines +202 to +207
push(chunk: Uint8Array | string): DiscoveryValidity {
if (this._error) return this.result;
this.bytesSeen +=
typeof chunk === "string"
? DISCOVERY_TEXT_ENCODER.encode(chunk).byteLength
: chunk.byteLength;

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 The byte counter for string chunks calls DISCOVERY_TEXT_ENCODER.encode(chunk).byteLength on every push, which allocates and immediately discards a Uint8Array. This allocation happens even after _ready becomes true and the limit check no longer fires. Buffer.byteLength resolves the UTF-8 size in native code without an allocation.

Suggested change
push(chunk: Uint8Array | string): DiscoveryValidity {
if (this._error) return this.result;
this.bytesSeen +=
typeof chunk === "string"
? DISCOVERY_TEXT_ENCODER.encode(chunk).byteLength
: chunk.byteLength;
push(chunk: Uint8Array | string): DiscoveryValidity {
if (this._error) return this.result;
this.bytesSeen +=
typeof chunk === "string"
? Buffer.byteLength(chunk, "utf8")
: chunk.byteLength;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/proxy/discovery-validity.ts
Line: 202-207

Comment:
The byte counter for string chunks calls `DISCOVERY_TEXT_ENCODER.encode(chunk).byteLength` on every push, which allocates and immediately discards a `Uint8Array`. This allocation happens even after `_ready` becomes `true` and the limit check no longer fires. `Buffer.byteLength` resolves the UTF-8 size in native code without an allocation.

```suggestion
  push(chunk: Uint8Array | string): DiscoveryValidity {
    if (this._error) return this.result;
    this.bytesSeen +=
      typeof chunk === "string"
        ? Buffer.byteLength(chunk, "utf8")
        : chunk.byteLength;
```

How can I resolve this? If you propose a fix, please make it concise.

@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 (2)
src/app/v1/_lib/proxy/response-handler.ts (1)

1811-1813: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

复用已解析的 completionInspection,避免对同一份 allContent 二次解析。

第 1742 行已计算 completionInspection = inspectStreamCompletion(allContent, session.originalFormat),此处 hasStreamCompletionMarker(allContent, session.originalFormat) 会再次完整解析同一份内容(parseSSEData 及 Gemini 的 extractJsonChunks)。两者入参完全一致且语义等价(协议错误分支同样返回 hasMarker=false),在客户端中断的成功判定路径上对大流式响应重复解析属于不必要开销。

♻️ 复用已有解析结果
-    if (!hasStreamCompletionMarker(allContent, session.originalFormat)) {
+    if (!completionInspection.hasMarker) {
       return false;
     }
🤖 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/response-handler.ts` around lines 1811 - 1813,
在客户端中断的成功判定流程中,复用前面已计算的 completionInspection 结果替代
hasStreamCompletionMarker(allContent, session.originalFormat) 的重复解析;根据其
hasMarker 字段保持现有无完成标记时返回 false 的行为不变。
src/lib/ledger-backfill/trigger.sql (1)

260-290: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

routing_trace 补入 UPDATE OF 列集合。

fn_upsert_usage_ledger() 不会根据 routing_trace 重算 ledger,但当前触发语句的列集合缺失 routing_trace,若仅更新该字段会再次触发并增加不必要的冲突路径;将其显式纳入列集合后,该列更新不再触发重算。

🤖 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/ledger-backfill/trigger.sql` around lines 260 - 290, Update the
INSERT/UPDATE trigger definition for message_request by adding routing_trace to
the UPDATE OF column list, so routing_trace-only updates do not invoke
fn_upsert_usage_ledger().
🤖 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/v1/_lib/proxy/response-handler.ts`:
- Around line 1811-1813: 在客户端中断的成功判定流程中,复用前面已计算的 completionInspection 结果替代
hasStreamCompletionMarker(allContent, session.originalFormat) 的重复解析;根据其
hasMarker 字段保持现有无完成标记时返回 false 的行为不变。

In `@src/lib/ledger-backfill/trigger.sql`:
- Around line 260-290: Update the INSERT/UPDATE trigger definition for
message_request by adding routing_trace to the UPDATE OF column list, so
routing_trace-only updates do not invoke fn_upsert_usage_ledger().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a772b4a-a134-4270-9f0d-ab5926c4dbf5

📥 Commits

Reviewing files that changed from the base of the PR and between 0f412f8 and 671870b.

📒 Files selected for processing (120)
  • .env.example
  • docs/streaming-discovery.md
  • drizzle/0110_daffy_rawhide_kid.sql
  • drizzle/0111_happy_mauler.sql
  • drizzle/meta/0110_snapshot.json
  • drizzle/meta/0111_snapshot.json
  • drizzle/meta/_journal.json
  • messages/en/dashboard.json
  • messages/en/settings/config.json
  • messages/ja/dashboard.json
  • messages/ja/settings/config.json
  • messages/ru/dashboard.json
  • messages/ru/settings/config.json
  • messages/zh-CN/dashboard.json
  • messages/zh-CN/settings/config.json
  • messages/zh-TW/dashboard.json
  • messages/zh-TW/settings/config.json
  • package.json
  • src/actions/system-config.ts
  • src/actions/usage-logs.ts
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.test.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/DiscoveryTraceView.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/LogicTraceTab.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/index.ts
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/index.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/types.ts
  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.test.tsx
  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.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/[locale]/settings/config/_components/system-settings-form.tsx
  • src/app/[locale]/settings/config/page.tsx
  • src/app/api/admin/system-config/route.ts
  • src/app/api/v1/resources/system/handlers.ts
  • src/app/api/v1/resources/system/router.ts
  • src/app/v1/_lib/proxy/discovery-coordinator.ts
  • src/app/v1/_lib/proxy/discovery-validity.ts
  • src/app/v1/_lib/proxy/error-handler.ts
  • src/app/v1/_lib/proxy/forwarder.ts
  • src/app/v1/_lib/proxy/provider-selector.ts
  • src/app/v1/_lib/proxy/response-handler.ts
  • src/app/v1/_lib/proxy/session.ts
  • src/app/v1/_lib/proxy/stream-finalization.ts
  • src/drizzle/schema.ts
  • src/instrumentation.ts
  • src/lib/api-client/v1/openapi-types.gen.ts
  • src/lib/api/v1/_shared/error-envelope.ts
  • src/lib/api/v1/_shared/request-body.ts
  • src/lib/api/v1/schemas/system-config.ts
  • src/lib/config/env.schema.ts
  • src/lib/config/system-settings-cache.ts
  • src/lib/ledger-backfill/trigger.sql
  • src/lib/lifecycle/shutdown.ts
  • src/lib/observability/discovery-metrics.ts
  • src/lib/redis/client.ts
  • src/lib/redis/live-chain-store.storage.test.ts
  • src/lib/redis/live-chain-store.test.ts
  • src/lib/redis/live-chain-store.ts
  • src/lib/redis/lua-scripts.ts
  • src/lib/redis/session-binding.ts
  • src/lib/session-manager.ts
  • src/lib/session-tracker.ts
  • src/lib/utils/provider-chain-display.test.ts
  • src/lib/utils/provider-chain-display.ts
  • src/lib/validation/discovery-settings.ts
  • src/lib/validation/schemas.ts
  • src/repository/_shared/transformers.ts
  • src/repository/message-write-buffer.ts
  • src/repository/message.ts
  • src/repository/routing-trace-outbox.ts
  • src/repository/routing-trace-persistence.ts
  • src/repository/system-config.ts
  • src/repository/usage-logs.ts
  • src/types/message.ts
  • src/types/routing-trace.ts
  • src/types/system-config.ts
  • tests/api/v1/system/system-config.test.ts
  • tests/configs/integration.config.ts
  • tests/configs/session-binding.config.ts
  • tests/integration/proxy-hedge-lifecycle.test.ts
  • tests/integration/session-binding-versioning-redis.test.ts
  • tests/unit/actions/system-config-save.test.ts
  • tests/unit/api/admin-system-config-route.test.ts
  • tests/unit/lib/redis/client.test.ts
  • tests/unit/lib/redis/live-chain-store.test.ts
  • tests/unit/lib/redis/session-binding.test.ts
  • tests/unit/lib/session-manager-binding-smart.test.ts
  • tests/unit/lib/session-manager-content-hash.test.ts
  • tests/unit/lib/session-manager-terminate-provider-sessions.test.ts
  • tests/unit/lib/session-manager-terminate-session.test.ts
  • tests/unit/lib/session-manager-versioned-binding.test.ts
  • tests/unit/lib/session-tracker-cleanup.test.ts
  • tests/unit/lib/shutdown.test.ts
  • tests/unit/proxy/discovery-coordinator.test.ts
  • tests/unit/proxy/discovery-validity.test.ts
  • tests/unit/proxy/error-handler-durable-persistence.test.ts
  • tests/unit/proxy/hedge-winner-dedup.test.ts
  • tests/unit/proxy/provider-selector-cross-type-model.test.ts
  • tests/unit/proxy/provider-selector-group-priority.test.ts
  • tests/unit/proxy/provider-selector-model-mismatch-binding.test.ts
  • tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts
  • tests/unit/proxy/proxy-forwarder-provider-session-release.test.ts
  • tests/unit/proxy/response-handler-abort-listener-cleanup.test.ts
  • tests/unit/proxy/response-handler-client-abort-drain.test.ts
  • tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts
  • tests/unit/proxy/response-handler-exported-finalizers.test.ts
  • tests/unit/proxy/response-handler-gemini-stream-passthrough-timeouts.test.ts
  • tests/unit/proxy/routing-trace.test.ts
  • tests/unit/proxy/terminal-outcome-contract.test.ts
  • tests/unit/repository/message-terminal-write-apis.test.ts
  • tests/unit/repository/message-write-buffer.test.ts
  • tests/unit/repository/routing-trace-outbox.test.ts
  • tests/unit/repository/system-config-degradation-ladder.test.ts
  • tests/unit/repository/system-config-update-missing-columns.test.ts
  • tests/unit/types/routing-trace.test.ts
  • tests/unit/usage-ledger/trigger.test.ts
  • tests/unit/validation/system-settings-discovery.test.ts

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 671870b6f3

ℹ️ 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/lib/session-manager.ts Outdated
sessionId: string,
keyId: number
): Promise<SessionBindingResult> {
const redis = getRedisClient();

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 Discovery Redis usable when rate limits are disabled

When ENABLE_RATE_LIMIT=false but Redis is configured for session tracking, the capability probe can still succeed because currentRedisClient() passes allowWhenRateLimitDisabled: true; however this wrapper reopens Redis through the default getRedisClient(), so prepareStreamingDiscovery() gets redis_not_ready from getSessionBindingSnapshot() and skips Discovery before taking a lease. Pass the same override in the versioned binding/lease wrappers so Discovery works independently of the rate-limit toggle.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot added the size/XL Extra Large PR (> 1000 lines) label Jul 23, 2026
}
this.deferredOrdinary.delete(acknowledgement.id);
const existing = this.pending.get(acknowledgement.id);
this.setPending(

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] Deferred ordinary patches can still update a finalized row after the post-terminal routing-trace ACK settles.

Why this is a problem: The new deferral path is supposed to keep late ordinary fields out of the trace-only write so terminal and billing data stay behind the terminal status fence. releaseDeferredOrdinary() requeues that deferred patch as a normal write, though, so the follow-up flush runs without the status_code IS NULL fence. A late costUsd, durationMs, or other ordinary patch can therefore still mutate an already-finalized request after updateMessageRequestRoutingTrace() starts using the post-terminal path.

Suggested fix:

private releaseDeferredOrdinary(acknowledgement: DurableAcknowledgement): void {
  if (acknowledgement.writeScope !== "post-terminal-metadata") return;
  const patch = this.deferredOrdinary.get(acknowledgement.id);
  if (!patch) return;
  this.deferredOrdinary.delete(acknowledgement.id);

  void this.enqueueDurably(acknowledgement.id, patch, { writeScope: "terminal" }).catch(
    (error) => {
      logger.error("[MessageRequestWriteBuffer] Failed to replay deferred terminal patch", {
        messageRequestId: acknowledgement.id,
        error: error instanceof Error ? error.message : String(error),
      });
    }
  );
}

return Response.json({ error: "discoveryWindowInvalid", errorCode }, { status: 400 });
}
const firstError = error.issues[0];
return Response.json({ error: firstError.message || "数据验证失败" }, { status: 400 });

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] The generic Discovery validation branch drops the structured errorCode and returns the raw token instead.

Why this is a problem: getDiscoveryValidationErrorCode() already distinguishes DISCOVERY_SETTINGS_INVALID from the window check, but this branch falls through to firstError.message. That makes /api/admin/system-config respond with { "error": "DISCOVERY_SETTINGS_INVALID" } for out-of-range Discovery fields instead of the stable { errorCode: "DISCOVERY_SETTINGS_INVALID" } contract used everywhere else in this PR. Clients cannot reliably localize or branch on that failure anymore.

Suggested fix:

if (error instanceof z.ZodError) {
  const errorCode = getDiscoveryValidationErrorCode(error.issues);
  if (errorCode === DISCOVERY_WINDOW_INVALID_ERROR_CODE) {
    return Response.json({ error: "discoveryWindowInvalid", errorCode }, { status: 400 });
  }
  if (errorCode === DISCOVERY_SETTINGS_INVALID_ERROR_CODE) {
    return Response.json({ error: "discoverySettingsInvalid", errorCode }, { status: 400 });
  }
  const firstError = error.issues[0];
  return Response.json({ error: firstError.message || "数据验证失败" }, { status: 400 });
}

@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

This PR lands a very large Discovery stack across proxy runtime, Redis/session binding, persistence, and dashboard surfaces. I focused on the new code paths that own request finalization and the new Discovery settings validation flow. The main issues I found are a finalized-row ownership regression in the async message writer and an inconsistent admin API error contract for Discovery validation.

PR Size: XL

  • Lines changed: 37116
  • Files changed: 120
  • Split suggestions: separate Redis/session-binding + migrations, proxy Discovery runtime/finalization, and dashboard/API/i18n surface changes into smaller review stacks where possible.

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)

  • src/repository/message-write-buffer.ts:807 - deferred ordinary patches are replayed as unfenced writes after a post-terminal routing-trace ACK, so late costUsd/durationMs updates can still mutate finalized rows.
  • src/app/api/admin/system-config/route.ts:150 - the admin config route drops the structured DISCOVERY_SETTINGS_INVALID errorCode and returns the raw token instead, making Discovery validation failures harder to localize and handle consistently.

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 #1359, applied the size/XL label, and posted the summary review plus 2 inline comments.

Main findings:

  • src/repository/message-write-buffer.ts:807 replays deferred ordinary patches as unfenced writes after post-terminal routing-trace ACKs, so late costUsd/durationMs updates can still mutate finalized rows.
  • src/app/api/admin/system-config/route.ts:150 returns the raw DISCOVERY_SETTINGS_INVALID token instead of a structured errorCode, which breaks the new Discovery validation contract.

If you want, I can also draft the follow-up tests for those two paths.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

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

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

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

Labels

size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants