Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,31 @@ Cycle 6 may update the contributor branch only when:
- two consecutive post-change `bun run test` executions pass;
- the focused release-helper run is supporting evidence, not sole closure; and
- required Cross-platform CI is green on the exact pushed head.

## 2026-07-24 — Cycle 6 investigation result

- Live contributor head at takeover: `5f84301252865dc6f792d3272c2fe0db6d09eb0e`.
- `CYCLE6_BASE_SHA`: `c63589ccfe9e053d92acc029f55be0a809fb6fca` (`origin/dev` after the cycle-start fetch).
- Rebased pre-repair head: `8feb0980`; `git range-diff` mapped both contributor commits exactly (`=`) to `458b3a39` and `8feb0980`.
- Environment: macOS arm64 (`Darwin 25.6.0`), Bun `1.3.14`.
- Evidence directory: `devlog/_plan/260724_bugfix_train/evidence/cycle6-pr337-root/`.
- The contributor's retained public report contains only the aggregate `3780 passed, 4 skipped, 4 failed` and says the four failures were in this five-test file. It does not retain the four failing names, assertion text, stderr, or first-failure line. The original four failure identities therefore remain unresolved.

### Inferred current candidates — not recovered failures

The four success-path cases below are investigation candidates only because they are the four cases in the current five-test file that require a zero release-helper exit. They are not recovered names for the originally reported failures and cannot receive per-failure classifications without the missing original reporter evidence.

| Inferred current candidate | Original failing log/line | Current rerun evidence | Classification | Causal evidence | Owner path | Disposition |
|---|---|---|---|---|---|---|
| `preflight runs typecheck, test suite, and privacy scan before version bump on main dry-runs` | Unavailable; current source line 216 is not an original failure line | PR: standalone 3/3 + full 3/3 pass; base: standalone 1/1 + full 2/2 pass | Unresolved — original failure identity unavailable | This current case passed in every recorded PR/base order; it does not identify an original failure | `tests/release-helper.test.ts` | No per-failure disposition; suite-level unresolved |
| `preview branch still defaults to preview tag and dry-run dispatch` | Unavailable; current source line 250 is not an original failure line | PR: standalone 3/3 + full 3/3 pass; base: standalone 1/1 + full 2/2 pass | Unresolved — original failure identity unavailable | This current case passed in every recorded PR/base order; it does not identify an original failure | `tests/release-helper.test.ts` | No per-failure disposition; suite-level unresolved |
| `dispatch pins the audited release SHA via expected-sha` | Unavailable; current source line 263 is not an original failure line | PR: standalone 3/3 + full 3/3 pass; base: standalone 1/1 + full 2/2 pass | Unresolved — original failure identity unavailable | This current case passed in every recorded PR/base order; it does not identify an original failure | `tests/release-helper.test.ts` | No per-failure disposition; suite-level unresolved |
| `aborts before dispatch when the remote branch moved during the CI wait` | Unavailable; current source line 275 is not an original failure line | PR: standalone 3/3 + full 3/3 pass; base: standalone 1/1 + full 2/2 pass | Unresolved — original failure identity unavailable | This current case passed in every recorded PR/base order; it does not identify an original failure | `tests/release-helper.test.ts` | No per-failure disposition; suite-level unresolved |

### Current green rerun evidence

- PR initial/repeats: `pr337-root-initial.log`, `pr337-root-repeat-1.log`, `pr337-root-repeat-2.log` — each `3861 pass / 0 fail`.
- PR standalone: `release-helper-1.log`, `release-helper-2.log`, `release-helper-3.log` — each `5 pass / 0 fail`.
- Exact-base comparison: `baseline-release-helper.log` (`5 pass / 0 fail`), `baseline-root-1.log`, `baseline-root-2.log` (each `3861 pass / 0 fail`).

The current reruns do not provide evidence for a release-helper source change in this scoped PR: the investigated current cases and full suites were green at the recorded PR and base heads, and no causal PR diff path was found. They do not reconstruct or resolve the original four failures. Their names, assertions, stderr, and first failing lines remain unresolved unless the original CI or test-reporter metadata is recovered. Accordingly, the historical suite-level disposition remains unresolved, and the final exact-head root gates are still required without changing release code or tests.
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ bun run dev:gui
| **Codex autostart** | Allow an already-installed Codex launcher shim to run `ocx ensure`. This toggle does not install a shim or background service. |
| **Providers** | Add, edit, enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. |
| **Add provider** | Search registry-backed presets for account login, API-key services, local servers, or a custom endpoint. |
| **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, and configure quota auto-switch and transient-failure failover. |
| **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, enable or disable quota auto-switch, set its 1–100% threshold, and configure transient-failure failover. |
| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. |
| **Models** | Toggle native GPT and routed models, set provider allowlists and context caps, choose v1/base/v2, and configure the v2 thread limit. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. |
| **Logs** | Auto-refresh recent requests with tokens, requested effort, resolved model, provider, status, request id, duration, and error details. |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/ja/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ bun run dev:gui
| **Codex 自動起動** | インストール済み Codex launcher shim に `ocx ensure` の実行を許可します。このトグルは shim やバックグラウンドサービスをインストールしません。 |
| **プロバイダー** | プロバイダーを追加、編集、有効化/無効化、削除し、対応する OAuth アカウントプールと API キープールを管理します。 |
| **プロバイダー追加** | レジストリベースのプリセットからアカウントログイン、API キーサービス、ローカルサーバー、custom エンドポイントを検索します。 |
| **Codex 認証** | ChatGPT/Codex プールアカウントを追加し、次回セッションアカウントを選び、5 時間 / 週間 / 30 日クォータを更新し、クォータ自動切り替えと一時的失敗フェイルオーバーを設定します。 |
| **Codex 認証** | ChatGPT/Codex プールアカウントを追加し、次回セッションアカウントを選び、5 時間 / 週間 / 30 日クォータを更新し、クォータ自動切り替えのオン/オフと 1~100% のしきい値、一時的失敗フェイルオーバーを設定します。 |
| **サブエージェント** | `spawn_agent` オーバーライド一覧にネイティブまたはルーティングモデルを最大 5 つまで優先公開します。 |
| **モデル** | ネイティブ GPT とルーティングモデルをオン/オフし、プロバイダー許可リストとコンテキスト上限、v1/base/v2、v2 スレッド数を設定します。 |
| **ログ** | トークン、リクエスト強度、実際のモデル、プロバイダー、状態、リクエスト ID、所要時間、エラー詳細を含む最近のリクエストを自動更新します。 |
Expand Down Expand Up @@ -69,7 +69,7 @@ bun run dev:gui
- アカウントを直接選ぶと次の新規 Codex セッションから変わります。すでにアカウントが紐付いた thread はこの手動切り替えだけでは
途中で移動しません。
- Thread affinity がリクエストごとにアカウントが揺れるのを防ぎます。クォータ自動切り替えがオンなら長く
実行される thread も定期的に再評価します。関連使用量がしきい値を超え、使用量が確実により低い
実行される thread も定期的に再評価します。関連使用量がしきい値以上で、使用量が確実により低い
健全アカウントがあればそのアカウントに再紐付けできます。
- 新規セッションは使用量が最も低い健全アカウントを選べます。有料プランは既知の 5 時間、週間、30 日
枠のうち最も高い使用率でスコア付けし、Go/Free プランは 30 日枠のみ使います。
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/ko/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ bun run dev:gui
| **Codex 자동 시작** | 이미 설치된 Codex launcher shim이 `ocx ensure`를 실행하도록 허용합니다. 이 토글은 shim이나 백그라운드 서비스를 설치하지 않습니다. |
| **Providers** | 프로바이더를 추가, 편집, 활성화/비활성화, 제거하고, 지원되는 OAuth 계정 풀과 API key 풀을 관리합니다. |
| **Add provider** | 레지스트리 기반 프리셋에서 계정 로그인, API key 서비스, 로컬 서버, custom endpoint를 검색합니다. |
| **Codex Auth** | ChatGPT/Codex 풀 계정을 추가하고, 다음 세션 계정을 선택하고, 5시간 / 주간 / 30일 할당량을 갱신하며, 할당량 자동 전환과 일시적 실패 failover를 설정합니다. |
| **Codex Auth** | ChatGPT/Codex 풀 계정을 추가하고, 다음 세션 계정을 선택하고, 5시간 / 주간 / 30일 할당량을 갱신하며, 할당량 자동 전환을 켜거나 끄고 1~100% 임계값과 일시적 실패 failover를 설정합니다. |
| **Subagents** | `spawn_agent` override 목록에 네이티브 또는 라우팅 모델을 최대 5개까지 우선 노출합니다. |
| **Models** | 네이티브 GPT와 라우팅 모델을 켜고 끄고, 프로바이더 allowlist와 컨텍스트 상한, v1/base/v2, v2 thread 수를 설정합니다. |
| **Logs** | 토큰, 요청 강도, 실제 모델, 프로바이더, 상태, 요청 id, 소요 시간, 오류 상세가 포함된 최근 요청을 자동 갱신합니다. |
Expand Down Expand Up @@ -69,7 +69,7 @@ Dashboard의 **Sub-agent delegation** 선택기는 `injectionModel`과 선택적
- 계정을 직접 고르면 다음 새 Codex 세션부터 바뀝니다. 이미 계정이 묶인 thread는 이 수동 전환만으로
중간에 이동하지 않습니다.
- Thread affinity가 요청마다 계정이 흔들리는 일을 막습니다. 할당량 자동 전환이 켜져 있으면 오래
실행되는 thread도 주기적으로 다시 평가합니다. 관련 사용량이 임계값을 넘고 사용량이 확실히 더 낮은
실행되는 thread도 주기적으로 다시 평가합니다. 관련 사용량이 임계값 이상이고 사용량이 확실히 더 낮은
정상 계정이 있으면 그 계정으로 다시 묶일 수 있습니다.
- 새 세션은 사용량이 가장 낮은 정상 계정을 고를 수 있습니다. 유료 플랜은 알려진 5시간, 주간, 30일
창 중 가장 높은 사용률로 점수를 매기고, Go/Free 플랜은 30일 창만 사용합니다.
Expand Down
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/ru/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ bun run dev:gui
| **Автозапуск Codex** | Разрешает уже установленному launcher shim Codex выполнять `ocx ensure`. Переключатель не устанавливает shim или фоновую службу. |
| **Providers** | Добавление, редактирование, включение/отключение и удаление провайдеров; управление пулами OAuth-аккаунтов и пулами API-ключей там, где они поддерживаются. |
| **Add provider** | Поиск по пресетам из реестра: вход по аккаунту, сервисы с API-ключом, локальные серверы или пользовательская конечная точка. |
| **Codex Auth** | Добавление аккаунтов пула ChatGPT/Codex, выбор аккаунта для следующей сессии, обновление квот 5 ч / недельных / 30-дневных, настройка автопереключения по квоте и failover при временных сбоях. |
| **Codex Auth** | Добавление аккаунтов пула ChatGPT/Codex, выбор аккаунта для следующей сессии, обновление квот 5 ч / недельных / 30-дневных, включение или отключение автопереключения, настройка его порога 1–100% и failover при временных сбоях. |
| **Subagents** | Выделение до пяти «голых» нативных или маршрутизируемых моделей с пространством имён в списке переопределений `spawn_agent`. |
| **Models** | Включение и отключение нативных GPT и маршрутизируемых моделей, настройка списков разрешённых провайдеров и лимитов контекста, выбор v1/base/v2 и настройка лимита потоков v2. |
| **Logs** | Автообновляемый список недавних запросов: токены, запрошенный уровень рассуждений, фактическая модель, провайдер, статус, id запроса, длительность и подробности ошибок. |
Expand Down
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/zh-cn/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ bun run dev:gui
| **Codex 自动启动** | 允许已安装的 Codex launcher shim 运行 `ocx ensure`。此开关不会安装 shim 或后台服务。 |
| **Providers** | 添加、编辑、启用/禁用、删除 provider,并在支持时管理 OAuth 账号池和 API key 池。 |
| **Add provider** | 搜索 registry preset,选择账号登录、API key 服务、本地服务器或自定义 endpoint。 |
| **Codex Auth** | 添加 ChatGPT/Codex 池账号,选择下一 session 的账号,刷新 5h / 每周 / 30d 配额,并设置配额自动切换和临时故障 failover。 |
| **Codex Auth** | 添加 ChatGPT/Codex 池账号,选择下一 session 的账号,刷新 5h / 每周 / 30d 配额,启用或停用配额自动切换,设置其 1–100% 阈值和临时故障 failover。 |
| **Subagents** | 在 `spawn_agent` override 列表中置顶最多五个原生或路由模型。 |
| **Models** | 开关原生 GPT 与路由模型,配置 provider allowlist、上下文上限、v1/base/v2 以及 v2 thread 数量。 |
| **Logs** | 自动刷新近期请求,显示 token、请求强度、实际模型、provider、状态、request id、耗时和错误详情。 |
Expand Down
84 changes: 84 additions & 0 deletions gui/src/codex-auto-switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
export const DEFAULT_AUTO_SWITCH_THRESHOLD = 80;

const AUTO_SWITCH_PUT_TIMEOUT_MS = 10_000;

export type AutoSwitchFetch = (input: string, init: RequestInit) => Promise<Response>;

export function normalizeAutoSwitchThreshold(value: unknown): number {
return typeof value === "number"
&& Number.isInteger(value)
&& value >= 0
&& value <= 100
? value
: DEFAULT_AUTO_SWITCH_THRESHOLD;
}

export function parseEnabledAutoSwitchThreshold(value: string): number | null {
const trimmed = value.trim();
if (!/^\d+$/.test(trimmed)) return null;
const threshold = Number(trimmed);
return threshold >= 1 && threshold <= 100 ? threshold : null;
}

export function nextAutoSwitchThreshold(current: number, lastEnabled: number): number {
if (current > 0) return 0;
return Number.isInteger(lastEnabled) && lastEnabled >= 1 && lastEnabled <= 100
? lastEnabled
: DEFAULT_AUTO_SWITCH_THRESHOLD;
}

export type AutoSwitchThresholdReadDisposition = "apply" | "defer" | "ignore";

export function autoSwitchThresholdReadDisposition(
editing: boolean,
saving: boolean,
startedRevision: number,
currentRevision: number,
): AutoSwitchThresholdReadDisposition {
if (startedRevision !== currentRevision) return "ignore";
return editing || saving ? "defer" : "apply";
}

export interface AutoSwitchTogglePlan {
threshold: number;
lastEnabled: number;
}

/**
* Disabling is one server write. A valid dirty draft becomes the page-lifetime
* restore value, so re-enabling can persist it without a partial two-write
* failure state.
*/
export function planAutoSwitchToggleWrite(
current: number,
draft: string,
lastEnabled: number,
): AutoSwitchTogglePlan {
if (current <= 0) {
const threshold = nextAutoSwitchThreshold(current, lastEnabled);
return { threshold, lastEnabled: threshold };
}
const restoreThreshold = parseEnabledAutoSwitchThreshold(draft)
?? nextAutoSwitchThreshold(0, lastEnabled);
return { threshold: 0, lastEnabled: restoreThreshold };
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export async function putAutoSwitchThreshold(
apiBase: string,
threshold: number,
fetchImpl: AutoSwitchFetch = (input, init) => fetch(input, init),
timeoutMs = AUTO_SWITCH_PUT_TIMEOUT_MS,
): Promise<boolean> {
if (!Number.isInteger(threshold) || threshold < 0 || threshold > 100) return false;
try {
const response = await fetchImpl(`${apiBase}/api/codex-auth/auto-switch`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ threshold }),
signal: AbortSignal.timeout(timeoutMs),
});
return response.ok;
} catch {
return false;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading
Loading