Skip to content

feat: add provider scheduled active time & remove joinClaudePool#844

Merged
ding113 merged 2 commits intodevfrom
feat/provider-scheduled-active-time
Feb 27, 2026
Merged

feat: add provider scheduled active time & remove joinClaudePool#844
ding113 merged 2 commits intodevfrom
feat/provider-scheduled-active-time

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Feb 27, 2026

Summary

  • Add HH:mm-based active time windows to providers for time-based routing control (same-day & cross-day schedules with system timezone)
  • Remove deprecated joinClaudePool column/feature
  • Single consolidated migration (0077) for both changes

Changes

Core Logic

  • src/lib/utils/provider-schedule.ts: isProviderActiveNow() - O(1) pure function with NaN fail-open defense
  • Same-day: start <= now < end; Cross-day: now >= start || now < end

Database

  • Single migration: ADD active_time_start/active_time_end VARCHAR(5), DROP join_claude_pool

Type System

  • 7 interfaces updated: Provider, ProviderDisplay, Create/UpdateProviderData, BatchPatch types

Proxy Selection (Hot Path)

  • schedule_inactive filter reason in provider-selector.ts
  • Timezone resolved once per request via cached system settings

Batch Edit

  • HH:mm regex validation in patch contract (isValidSetValue)
  • Set/clear support for both fields

UI

  • Provider form: schedule toggle, time pickers, cross-day hint, timezone note
  • Provider list: Clock icon schedule badge
  • Batch edit: dirty field mapping

i18n

  • 5 languages: zh-CN, zh-TW, en, ja, ru

Tests

  • 25 schedule tests (null, same-day, cross-day, boundary, timezone, malformed input)
  • 10 batch patch tests (set, clear, reject non-string, reject invalid format, combined)

Test plan

  • bun run typecheck - 0 errors
  • bun run lint - clean
  • bun run test - 3425 passed (360 files)
  • bun run build - production build success
  • Manual: create provider with schedule, verify proxy selector filtering
  • Manual: batch edit schedule on multiple providers
  • Manual: verify cross-day schedule (e.g., 22:00-08:00)
  • Manual: verify provider list shows schedule badge

Greptile Summary

Adds HH:mm-based scheduled active time windows to providers for time-based routing control, with comprehensive validation and UI support. Also removes the deprecated joinClaudePool feature in a single consolidated migration.

Key Changes:

  • Core scheduling logic in provider-schedule.ts with O(1) pure function, fail-open defense for malformed data, and support for same-day and cross-day schedules
  • Hot path integration in provider selector with timezone resolution cached once per request
  • Session provider clearing when outside active window prevents session reuse from bypassing time-based routing
  • Comprehensive validation: HH:mm regex, both-or-neither enforcement, equal-time rejection
  • Full batch edit support with set/clear operations and HH:mm format validation
  • UI with schedule toggle, time pickers, cross-day hint, timezone note, and clock badge in provider list
  • i18n coverage across 5 languages (zh-CN, zh-TW, en, ja, ru)
  • Test coverage: 25 schedule tests + 10 batch patch tests

Code Quality:

  • All 3425 tests pass, typecheck clean, lint clean, production build successful
  • Well-documented with clear comments on fail-open behavior and timezone handling
  • Proper defensive programming with NaN checks and malformed input handling

Confidence Score: 5/5

  • Safe to merge - well-tested feature addition with comprehensive validation and defensive programming
  • Excellent implementation quality with 3425 passing tests, comprehensive validation at multiple layers (Zod schemas, batch patch contract, runtime checks), defensive fail-open behavior for malformed data, clear documentation, and proper timezone handling. No breaking changes detected.
  • No files require special attention - all changes are well-implemented with proper test coverage

Important Files Changed

Filename Overview
src/lib/utils/provider-schedule.ts New core scheduling logic with fail-open defense, same-day/cross-day support, comprehensive docs
src/app/v1/_lib/proxy/provider-selector.ts Integrated schedule filtering in hot path with session clearing, timezone resolved once per request
src/lib/validation/schemas.ts Added schedule field validation with HH:mm regex, both-or-neither enforcement, equal-time rejection
src/lib/provider-patch-contract.ts Added schedule fields to batch patch system with HH:mm validation and clear/set support
tests/unit/lib/utils/provider-schedule.test.ts Comprehensive 25 tests covering null, same-day, cross-day, boundaries, timezones, malformed input
drizzle/0077_nappy_giant_man.sql Migration adds active_time_start/end VARCHAR(5), drops join_claude_pool column
src/types/provider.ts Added activeTimeStart/End to 7 interfaces (Provider, ProviderDisplay, Create/Update/Batch types)
src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx Added schedule toggle, time pickers, cross-day hint, timezone note with proper state management

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Provider Request] --> B{Session Provider Exists?}
    B -->|Yes| C[Check Schedule Active]
    C -->|Inactive| D[Clear Session & Return Null]
    C -->|Active| E[Use Session Provider]
    B -->|No| F[Resolve System Timezone]
    F --> G[Filter Enabled Providers]
    G --> H{Check Schedule Active}
    H -->|Inactive| I[Skip Provider - schedule_inactive]
    H -->|Active| J[Format/Model Match]
    J --> K[Build Eligible List]
    K --> L[Priority Selection]
    
    M[Provider Schedule Logic] --> N{Both Times Null?}
    N -->|Yes| O[Always Active - Return True]
    N -->|No| P{Start == End?}
    P -->|Yes| Q[Zero Width - Return False]
    P -->|No| R[Parse HH:mm to Minutes]
    R --> S{Valid Format?}
    S -->|No| T[Fail Open - Return True]
    S -->|Yes| U{Start < End?}
    U -->|Yes| V[Same Day: start <= now < end]
    U -->|No| W[Cross Day: now >= start OR now < end]
Loading

Last reviewed commit: de1bdf7

…audePool

Add HH:mm-based active time windows (start/end) to providers for
time-based routing control. Supports same-day and cross-day schedules
with system timezone integration.

Also removes the deprecated joinClaudePool column/feature.

Key changes:
- Core: isProviderActiveNow() with NaN fail-open defense
- Schema: active_time_start/end columns, drop join_claude_pool (single migration)
- Types: 7 interfaces updated across Provider/Batch/Create/Update
- Proxy: schedule_inactive filter in provider selection hot path
- Batch: HH:mm regex validation in patch contract
- UI: schedule toggle, time pickers, cross-day hint, list badge
- i18n: 5 languages (zh-CN, zh-TW, en, ja, ru)
- Tests: 25 schedule tests + 10 batch patch tests
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

本PR为提供商引入“活跃时间窗口”调度:新增数据库字段 active_time_start/active_time_end、前端表单与多语言文案、验证与补丁支持、调度判断工具及在提供商选择器中的调度过滤;同时移除 join_claude_pool 字段。

Changes

Cohort / File(s) Summary
数据库迁移与模式
drizzle/0077_nappy_giant_man.sql, drizzle/meta/_journal.json, src/drizzle/schema.ts
providers 表新增 active_time_startactive_time_end (varchar(5)),移除 join_claude_pool,并追加迁移日志。
多语言翻译文件
messages/en/settings/providers/*, messages/ja/settings/providers/*, messages/ru/settings/providers/*, messages/zh-CN/settings/providers/*, messages/zh-TW/settings/providers/*
为 batchEdit 添加 activeTimeStart/activeTimeEnd 标签;在 form/sections 添加 activeTime 调度 UI 文案;在 list.json 添加 schedule 键,覆盖 5 种语言。
类型与接口
src/types/provider.ts, src/types/message.ts, src/types/message.ts
将 provider 类型及 display、patch、create/update 数据结构扩展为包含 active_time_start/active_time_end(或 camelCase 映射);在 ProviderChainItem.reason 中加入 "schedule_inactive"
后端/仓储层
src/repository/provider.ts, src/repository/_shared/transformers.ts
仓储层读写映射新字段,创建/更新/批量更新路径包含 activeTimeStart/activeTimeEnd 并在返回结果中暴露。
Actions 与 API 层
src/actions/providers.ts
在 providers 返回/请求数据结构中加入 activeTimeStart/activeTimeEnd,扩展 Add/Edit 输入与批量映射。
表单状态与 UI 组件
src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-types.ts, .../provider-form-context.tsx, .../index.tsx, .../sections/routing-section.tsx
在表单 state 与 reducer 中加入 activeTimeStart/activeTimeEnd、新增表单动作,提交时包含 active_time_start/active_time_end;在 routing 区块新增“计划活跃时间”卡片、开关与时间输入(含跨日提示与时区说明)。
批量编辑补丁机制
src/app/[locale]/settings/providers/_components/batch-edit/build-patch-draft.ts, src/lib/provider-patch-contract.ts, tests/unit/actions/providers-patch-contract.test.ts
补丁草稿构建器处理 active_time_start/active_time_end 的脏检查与清除;patch 合约新增字段、验证 HH:mm 格式、归一化与应用/清除行为;对应单元测试覆盖验证与映射。
提供商调度工具与验证
src/lib/utils/provider-schedule.ts, src/lib/validation/schemas.ts, src/app/v1/_lib/proxy/provider-selector.ts, tests/unit/lib/utils/provider-schedule.test.ts
新增 isProviderActiveNow 工具(支持同日/跨日/时区/容错);在创建/更新校验中加入两字段的格式及配对/不相等约束;在 provider-selector 中使用系统时区解析并基于活动窗口过滤/拒绝会话重用;新增对应单元测试。
列表展示
src/app/[locale]/settings/providers/_components/provider-rich-list-item.tsx
在提供商列表项加入计划时间徽章(Clock 图标),显示 activeTimeStart/activeTimeEnd

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地反映了本次PR的两个主要变更:添加提供商计划活跃时间功能和移除已弃用的joinClaudePool字段。
Description check ✅ Passed PR description clearly describes the scheduled active time feature, database migration, type system changes, proxy selection logic, batch edit support, UI components, i18n coverage, and comprehensive testing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/provider-scheduled-active-time

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

❤️ Share

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant new feature allowing administrators to define scheduled active time windows for providers, enhancing routing control and operational flexibility. Concurrently, it cleans up the codebase by removing a deprecated feature, joinClaudePool. The changes span database schema, core logic, API actions, UI components, and validation, all supported by new utility functions and comprehensive test coverage.

Highlights

  • Scheduled Active Time for Providers: Introduced HH:mm-based active time windows for providers, enabling time-based routing control with support for same-day and cross-day schedules based on the system timezone.
  • Removal of Deprecated Feature: The deprecated joinClaudePool column and associated feature have been removed from the system.
  • Database and Type System Updates: A single consolidated migration adds active_time_start and active_time_end columns to the providers table and drops join_claude_pool. Seven core interfaces related to providers have been updated to reflect these changes.
  • Proxy Selection Integration: The proxy selection hot path now includes a schedule_inactive filter reason, ensuring providers are only routed when within their active time window. System timezone is resolved once per request for efficiency.
  • UI and Batch Edit Enhancements: The provider form now includes a schedule toggle, time pickers, cross-day hints, and timezone notes. The provider list displays a clock icon schedule badge. Batch editing supports setting and clearing active time fields with HH:mm regex validation.
  • Comprehensive Testing: Extensive unit tests have been added, including 25 schedule tests covering various scenarios (null, same-day, cross-day, boundary, timezone, malformed input) and 10 batch patch tests.
Changelog
  • drizzle/0077_nappy_giant_man.sql
    • Added active_time_start and active_time_end columns to the providers table.
    • Removed the join_claude_pool column from the providers table.
  • drizzle/meta/_journal.json
    • Updated the Drizzle migration journal to include the new migration 0077_nappy_giant_man.
  • messages/en/settings/providers/batchEdit.json
    • Added new translation keys for 'Active Start Time' and 'Active End Time' for batch editing.
  • messages/en/settings/providers/form/sections.json
    • Added new translation keys for the 'Scheduled Active Time' section, including title, description, toggle labels, time labels, timezone note, and cross-day hint.
  • messages/en/settings/providers/list.json
    • Added a new translation key for 'Schedule'.
  • messages/ja/settings/providers/batchEdit.json
    • Added Japanese translation keys for 'スケジュール開始時刻' and 'スケジュール終了時刻'.
  • messages/ja/settings/providers/form/sections.json
    • Added Japanese translation keys for the 'スケジュール有効時間' section.
  • messages/ja/settings/providers/list.json
    • Added Japanese translation key for 'スケジュール'.
  • messages/ru/settings/providers/batchEdit.json
    • Added Russian translation keys for 'Время начала расписания' and 'Время окончания расписания'.
  • messages/ru/settings/providers/form/sections.json
    • Added Russian translation keys for the 'Расписание активности' section.
  • messages/ru/settings/providers/list.json
    • Added Russian translation key for 'Расписание'.
  • messages/zh-CN/settings/providers/batchEdit.json
    • Added Simplified Chinese translation keys for '调度开始时间' and '调度结束时间'.
  • messages/zh-CN/settings/providers/form/sections.json
    • Added Simplified Chinese translation keys for the '调度时间窗口' section.
  • messages/zh-CN/settings/providers/list.json
    • Added Simplified Chinese translation key for '调度'.
  • messages/zh-TW/settings/providers/batchEdit.json
    • Added Traditional Chinese translation keys for '排程開始時間' and '排程結束時間'.
  • messages/zh-TW/settings/providers/form/sections.json
    • Added Traditional Chinese translation keys for the '排程時間窗口' section.
  • messages/zh-TW/settings/providers/list.json
    • Added Traditional Chinese translation key for '排程'.
  • src/actions/providers.ts
    • Updated provider data structures and mapping functions to include activeTimeStart and activeTimeEnd fields.
  • src/app/[locale]/settings/providers/_components/batch-edit/build-patch-draft.ts
    • Modified to include logic for setting and clearing active_time_start and active_time_end in batch patch drafts.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx
    • Updated the provider form to pass active_time_start and active_time_end values.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context.tsx
    • Added new actions (SET_ACTIVE_TIME_START, SET_ACTIVE_TIME_END) and updated initial state and reducer logic to manage scheduled active times.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-types.ts
    • Extended RoutingState and ProviderFormAction types to include activeTimeStart and activeTimeEnd.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx
    • Implemented UI elements for 'Scheduled Active Time', including a toggle, time input fields, and conditional hints for cross-day schedules.
    • Imported the Clock icon for visual representation.
  • src/app/[locale]/settings/providers/_components/provider-rich-list-item.tsx
    • Imported the Clock icon.
    • Added logic to display a schedule badge with active time range on provider list items if a schedule is configured.
  • src/app/v1/_lib/proxy/provider-selector.ts
    • Integrated isProviderActiveNow function to filter providers based on their active schedule.
    • Added schedule_inactive as a reason for provider exclusion.
    • Resolved system timezone once per request to optimize active time checks.
  • src/drizzle/schema.ts
    • Modified the providers table schema to add activeTimeStart and activeTimeEnd as VARCHAR(5) columns.
    • Removed the joinClaudePool boolean column from the providers table.
  • src/lib/provider-patch-contract.ts
    • Added active_time_start and active_time_end to the list of patchable fields.
    • Updated CLEARABLE_FIELDS to allow clearing of active time fields.
    • Implemented validation for active_time_start and active_time_end to ensure HH:mm format.
    • Modified normalization and application functions to handle active time fields in batch patches.
  • src/lib/utils/provider-schedule.ts
    • Added a new utility file containing isProviderActiveNow function to determine if a provider is active within its scheduled time window, supporting same-day and cross-day schedules.
  • src/lib/validation/schemas.ts
    • Updated CreateProviderSchema and UpdateProviderSchema to include active_time_start and active_time_end fields.
    • Added Zod validation for HH:mm format and cross-field validation to ensure both active time fields are either set or null, and that start and end times are not identical.
  • src/repository/_shared/transformers.ts
    • Updated the toProvider transformer to map activeTimeStart and activeTimeEnd from the database.
  • src/repository/provider.ts
    • Modified various provider repository functions (createProvider, findProviderList, findAllProvidersFresh, findProviderById, updateProvider, updateProvidersBatch) to correctly handle the new activeTimeStart and activeTimeEnd fields.
  • src/types/message.ts
    • Added schedule_inactive as a possible reason for a provider chain item being filtered.
  • src/types/provider.ts
    • Updated multiple provider-related interfaces and types (ProviderBatchPatchField, ProviderBatchPatchDraft, ProviderBatchPatch, ProviderBatchApplyUpdates, Provider, ProviderDisplay, CreateProviderData, UpdateProviderData) to incorporate active_time_start and active_time_end.
  • tests/unit/actions/providers-patch-contract.test.ts
    • Added unit tests to verify the detection of changes for active_time_start and active_time_end in batch patches.
    • Included tests for accepting, clearing, and rejecting invalid values for active_time_start and active_time_end in batch patch operations.
  • tests/unit/lib/utils/provider-schedule.test.ts
    • Added a new test file with comprehensive unit tests for the isProviderActiveNow utility function, covering various scenarios including null inputs, same-day schedules, cross-day schedules, boundary conditions, timezone considerations, and malformed input handling.
Activity
  • The pull request introduces a new feature for provider scheduling and removes a deprecated one.
  • Extensive changes were made across the codebase, including database schema, API, UI, and validation.
  • New utility functions for schedule checking and timezone resolution were added.
  • All automated checks passed: bun run typecheck (0 errors), bun run lint (clean), bun run test (3425 passed in 360 files), and bun run build (production build success).
  • Manual testing is planned to verify the new scheduling functionality in the UI and proxy selection, including cross-day schedules and batch editing.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added enhancement New feature or request area:provider area:UI area:i18n size/XL Extra Large PR (> 1000 lines) labels Feb 27, 2026
@github-actions
Copy link
Contributor

🧪 测试结果

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

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

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new feature allowing providers to define scheduled active time windows, enhancing routing control. It also successfully removes the deprecated joinClaudePool column and associated logic. The implementation is thorough, covering database schema changes, API modifications, UI integration, and comprehensive unit tests for the new scheduling logic. The validation for time formats and consistency checks (e.g., both start and end times must be set or null) are well-handled, contributing to data integrity. Overall, the changes are well-executed and maintain a high standard of code quality.

Comment on lines +30 to +32
if (startTime == null || endTime == null) {
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The comment on line 19 states that validation ensures startTime and endTime are both set or both null. Given this, the explicit check if (startTime == null || endTime == null) on line 30 might be redundant if the validation is strictly enforced upstream. While it acts as a defensive fail-open, it could be simplified if the type system and validation guarantee that these will always be either both strings or both null. However, keeping it as a defensive measure is also acceptable.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx (1)

801-801: 考虑移除 default export。

根据编码规范,TypeScript 文件应优先使用命名导出(named exports)。该文件在第 723 行已有命名导出 ProviderForm,此处的 default export 可能是多余的。

建议的修改
-export default ProviderForm;

As per coding guidelines: "Prefer named exports over default exports"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`[locale]/settings/providers/_components/forms/provider-form/index.tsx
at line 801, The file currently re-exports ProviderForm as a default at the end;
remove the redundant default export line "export default ProviderForm;" so only
the existing named export ProviderForm remains, and update any imports elsewhere
to use a named import { ProviderForm } if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/v1/_lib/proxy/provider-selector.ts`:
- Around line 908-911: The new filter sets details = `outside active window
${p.activeTimeStart}-${p.activeTimeEnd}` which hardcodes an English message;
replace this with an i18n key or localized message lookup instead. Update the
branch in provider-selector.ts where isProviderActiveNow(p.activeTimeStart,
p.activeTimeEnd, systemTimezone) is false to set details to an i18n key (e.g.
"provider.schedule_inactive" with params for activeTimeStart and activeTimeEnd)
or call the localization helper used across the module, and ensure the reason
remains "schedule_inactive" while passing p.activeTimeStart and p.activeTimeEnd
into the i18n formatter so UI can render it in supported languages.
- Around line 849-861: findReusable currently returns bound/attached providers
without applying the same scheduling window check; update the findReusable code
path (function findReusable) to call
isProviderActiveNow(provider.activeTimeStart, provider.activeTimeEnd,
systemTimezone) and skip providers that are not active. Ensure systemTimezone is
available to findReusable (reuse the resolved const systemTimezone or resolve it
at the start of findReusable) so bound sessions cannot be selected outside their
activeTime window.

In `@src/lib/utils/provider-schedule.ts`:
- Around line 56-59: parseHHMM currently parses loosely and accepts malformed
values like "24:00" or "9:00", breaking fail-open logic; change parseHHMM to
perform strict format and range validation: require zero-padded HH:MM (use a
regex such as ^([01]\d|2[0-3]):([0-5]\d)$), only then parse hours/minutes and
return h*60+m, otherwise return NaN so callers that check isNaN keep the
fail-open behavior; update the implementation in parseHHMM in
provider-schedule.ts accordingly.

---

Nitpick comments:
In
`@src/app/`[locale]/settings/providers/_components/forms/provider-form/index.tsx:
- Line 801: The file currently re-exports ProviderForm as a default at the end;
remove the redundant default export line "export default ProviderForm;" so only
the existing named export ProviderForm remains, and update any imports elsewhere
to use a named import { ProviderForm } if needed.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between 40d7b62 and 031e2c4.

📒 Files selected for processing (36)
  • drizzle/0077_nappy_giant_man.sql
  • drizzle/meta/0077_snapshot.json
  • drizzle/meta/_journal.json
  • messages/en/settings/providers/batchEdit.json
  • messages/en/settings/providers/form/sections.json
  • messages/en/settings/providers/list.json
  • messages/ja/settings/providers/batchEdit.json
  • messages/ja/settings/providers/form/sections.json
  • messages/ja/settings/providers/list.json
  • messages/ru/settings/providers/batchEdit.json
  • messages/ru/settings/providers/form/sections.json
  • messages/ru/settings/providers/list.json
  • messages/zh-CN/settings/providers/batchEdit.json
  • messages/zh-CN/settings/providers/form/sections.json
  • messages/zh-CN/settings/providers/list.json
  • messages/zh-TW/settings/providers/batchEdit.json
  • messages/zh-TW/settings/providers/form/sections.json
  • messages/zh-TW/settings/providers/list.json
  • src/actions/providers.ts
  • src/app/[locale]/settings/providers/_components/batch-edit/build-patch-draft.ts
  • src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-types.ts
  • src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx
  • src/app/[locale]/settings/providers/_components/provider-rich-list-item.tsx
  • src/app/v1/_lib/proxy/provider-selector.ts
  • src/drizzle/schema.ts
  • src/lib/provider-patch-contract.ts
  • src/lib/utils/provider-schedule.ts
  • src/lib/validation/schemas.ts
  • src/repository/_shared/transformers.ts
  • src/repository/provider.ts
  • src/types/message.ts
  • src/types/provider.ts
  • tests/unit/actions/providers-patch-contract.test.ts
  • tests/unit/lib/utils/provider-schedule.test.ts

Comment on lines +908 to 911
} else if (!isProviderActiveNow(p.activeTimeStart, p.activeTimeEnd, systemTimezone)) {
reason = "schedule_inactive";
details = `outside active window ${p.activeTimeStart}-${p.activeTimeEnd}`;
} else if (
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

新增的过滤详情文本是硬编码英文。

details = \outside active window ...`` 建议改为可本地化文案或稳定的 i18n key/code。

As per coding guidelines "All user-facing strings must use i18n (5 languages supported: zh-CN, zh-TW, en, ja, ru). Never hardcode display text".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/v1/_lib/proxy/provider-selector.ts` around lines 908 - 911, The new
filter sets details = `outside active window
${p.activeTimeStart}-${p.activeTimeEnd}` which hardcodes an English message;
replace this with an i18n key or localized message lookup instead. Update the
branch in provider-selector.ts where isProviderActiveNow(p.activeTimeStart,
p.activeTimeEnd, systemTimezone) is false to set details to an i18n key (e.g.
"provider.schedule_inactive" with params for activeTimeStart and activeTimeEnd)
or call the localization helper used across the module, and ensure the reason
remains "schedule_inactive" while passing p.activeTimeStart and p.activeTimeEnd
into the i18n formatter so UI can render it in supported languages.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

After thorough analysis through 6 review perspectives (Comment Accuracy, Test Coverage, Error Handling, Type Safety, Logic/Security, and Code Clarity), no significant issues were identified in this PR.

PR Size: XL

  • Lines changed: 4,669 (4,649 additions, 20 deletions)
  • Files changed: 36

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Proper fail-open defense with logging
  • Type safety - Sound type definitions with correct nullability
  • Documentation accuracy - Comments match implementation
  • Test coverage - Comprehensive (35 new tests: 25 schedule + 10 batch patch)
  • Code clarity - Well-organized and readable

Key Implementation Highlights

Core Logic (src/lib/utils/provider-schedule.ts):

  • O(1) pure function with fail-open defense for malformed input
  • Correct same-day (start <= now < end) and cross-day (now >= start || now < end) logic
  • Timezone support via Intl.DateTimeFormat with cached system settings

Hot Path Integration (src/app/v1/_lib/proxy/provider-selector.ts):

  • Timezone resolved once per request (line 797)
  • Schedule filtering in provider candidate selection (line 860)
  • schedule_inactive filter reason tracking for observability

Validation (src/lib/validation/schemas.ts + src/lib/provider-patch-contract.ts):

  • HH:mm regex validation: ^([01][0-9]|2[0-3]):[0-5][0-9]$
  • Cross-validation: both fields must be set/cleared together, cannot be equal
  • Batch edit support with set/clear operations

Database (drizzle/0077_nappy_giant_man.sql):

  • Clean migration: adds active_time_start/active_time_end VARCHAR(5), drops deprecated join_claude_pool

Tests (tests/unit/lib/utils/provider-schedule.test.ts + tests/unit/actions/providers-patch-contract.test.ts):

  • 25 schedule tests: null inputs, same-day, cross-day, boundaries, timezone, malformed input defense
  • 10 batch patch tests: set, clear, reject non-string, reject invalid format, combined operations

Suggested Manual Test Plan (from PR author)

  • Create provider with schedule, verify proxy selector filtering
  • Batch edit schedule on multiple providers
  • Verify cross-day schedule (e.g., 22:00-08:00)
  • Verify provider list shows schedule badge

Automated review by Claude AI

1. Add schedule check in findReusable session reuse path to prevent
   bound sessions from bypassing active time window filtering.
   Clears session binding when provider is outside schedule.

2. Strict HH:mm validation in parseHHMM using regex - values like
   "24:00", "9:00", "99:99" now return NaN and trigger fail-open,
   instead of producing silently incorrect minute values.
@github-actions
Copy link
Contributor

🧪 测试结果

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

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

timeZone: timezone,
hour: "2-digit",
minute: "2-digit",
hour12: false,
Copy link
Contributor

@github-actions github-actions bot Feb 27, 2026

Choose a reason for hiding this comment

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

[CRITICAL] [LOGIC-BUG] Incorrect nowMinutes for 00:00-00:59 due to hour12: false (src/lib/utils/provider-schedule.ts:66)

Evidence (src/lib/utils/provider-schedule.ts:66):

hour12: false,

Why this is a problem: On Node/Intl, formatting 00:00-00:59 with hour12: false can produce an hour part of "24" (e.g. UTC 2020-01-01T00:30:00Z formats as 24:30). This code turns that into nowMinutes = 1440 + minute, so same-day windows that include midnight (e.g. 00:00-08:00, 00:00-23:59) are incorrectly treated as inactive for the first hour of the day.

Suggested fix:

function getCurrentMinutesInTimezone(now: Date, timezone: string): number {
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone: timezone,
    hour: "2-digit",
    minute: "2-digit",
    hourCycle: "h23",
  });

  const parts = formatter.formatToParts(now);
  const hourRaw = Number.parseInt(parts.find((p) => p.type === "hour")?.value ?? "0", 10);
  const minute = Number.parseInt(parts.find((p) => p.type === "minute")?.value ?? "0", 10);

  // Defensive: some runtimes can still emit "24" for midnight.
  const hour = hourRaw == 24 ? 0 : hourRaw;

  return hour * 60 + minute;
}

Add a regression test:

it("treats 00:30 as within 00:00-08:00", () => {
  const now = makeDate(0, 30, "UTC");
  expect(isProviderActiveNow("00:00", "08:00", "UTC", now)).toBe(true);
});

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR adds provider scheduled active time windows and removes joinClaudePool across DB, proxy selection, UI, and i18n. A critical bug in the new schedule utility can misclassify provider activity during 00:00-00:59, breaking schedules that start at midnight.

PR Size: XL

  • Lines changed: 4669
  • Files changed: 36
  • Split suggestion (XL): separate into (1) DB migration/schema/types, (2) proxy hot-path + schedule runtime logic, (3) UI/i18n, (4) tests.

Issues Found

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

  • src/lib/utils/provider-schedule.ts:66 - Intl.DateTimeFormat(..., { hour12: false }) can emit hour "24" for 00:xx, producing nowMinutes >= 1440 and breaking same-day windows that include midnight (confidence 98)

High Priority Issues (Should Fix)

  • None

Review Coverage

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

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

  • Identified PR #844 and applied label size/XL (4669 lines changed across 36 files).
  • Posted 1 inline review comment on src/lib/utils/provider-schedule.ts:66:
    • [CRITICAL] [LOGIC-BUG] Intl.DateTimeFormat(..., { hour12: false }) can emit hour "24" for 00:00–00:59, causing same-day windows that include midnight (e.g. 00:00-08:00, 00:00-23:59) to be treated as inactive.
    • Suggested fix: switch to hourCycle: "h23" (drop hour12) and defensively normalize 24 -> 0, plus add a regression test for 00:30 within 00:00-08:00.
  • Submitted the required PR review summary via gh pr review --comment.

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

Labels

area:i18n area:provider area:UI enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant