Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions devlog/_plan/260926_main_hard_lock_any_window/010_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 010 — Main-account 98% hard lock: any governing window blocks

## Problem
`getMainAccountHardLockStatus` (src/codex/main-account-hard-lock.ts) selects ONE window: 5h when any
5h field exists, else weekly, else monthly. A weekly reading of 98–100% with a 5h reading below 98 is
reported `ready`, so ocx keeps admitting main-account traffic until the weekly window is drained.

## Required policy (user, 2026-09-26)
- 5h >= 98 blocks, regardless of weekly.
- weekly >= 98 blocks immediately, even when 5h < 98.
- Either alone is sufficient.

## Diff-level plan
1. src/codex/main-account-hard-lock.ts
- Governing windows = [short] if present + [weekly] if present; monthly only when neither exists
(monthly-only accounts, unchanged; supplementary monthly still never governs).
- Classify each window: blocked (finite, 0..100, >= 98), ready (finite, 0..<98), unknown (missing/invalid).
- Any blocked -> blocked. resetAt = latest future reset among blocking windows; omitted when any
blocking window lacks a future reset (lock lasts until EVERY blocking window reads lower).
- Else any unknown -> unknown (admits, as today). Else ready.
- An invalid/unknown 5h reading can no longer mask a valid blocking weekly reading.
2. src/codex/quota.ts mergeAccountQuota: policy-mode guard symmetric to preserveKnownShort — an
observation with weeklyResetAt but no weeklyPercent keeps a retained blocking weekly tuple.
3. Tests (flip intended pins, add the user's cases):
- main-account-hard-lock-policy.test.ts: replace "5h wins", "expired 5h no fallback", "5h shape stays
unknown" with any-window cases: 5h97+w98 blocked, 5h98+w50 blocked, 5h97+w97 ready, release requires both,
5h-unknown+w99 blocked, 5h-unknown+w20 unknown, resetAt = latest; reset test line 70 now needs weekly 0.
- main-quota-evidence-validation.test.ts: "short zero keeps priority over weekly99" -> weekly99 blocks;
"rejected short usage" -> blocked.
- main-quota-window-observation.test.ts: weekly99 samples -> blocked, metadata-only -> unknown.
- weekly reset-only policy observation keeps weekly block (new).
4. Copy: gui/src/i18n/*.ts codexAuth.mainHardLockDesc (10 locales), docs-site en+ko providers-accounts,
structure/providers/openai-tiers.md paragraph.

## Out of scope
Monthly as an extra OR term for weekly/5h accounts; routing score; recovery probe cadence.

## Verification
Focused: the three hard-lock/quota test files + main-account-hard-lock-*.test.ts + account-usability/auth tests;
bun run typecheck; bun run structure:check; bun run test:changed; lint for gui i18n via typecheck.


## Audit fold (DeepSeek, NEAR-PASS)
- Folded: the governing window list is never empty — with no 5h/weekly the monthly slot is classified
even if absent, so a reading-less record stays unknown.
- Folded: the weekly reset-only guard excludes monthlyIsPrimaryWindow observations so monthly-primary
replacement still releases a weekly block.
- Folded: main-quota-window-observation weekly99 cases (incl. metadata-only, and final ready at 323/375)
flip to blocked because weekly99 is retained; test expectations follow the rule, not the old pins.
- Rebutted/residual: weekly evidence has no freshness clock. Unchanged from today's weekly-only accounts;
the per-minute blocked recovery sweep fetches WHAM, which carries the secondary window.
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ ocx login anthropic
Reserve입니다. 차단 중에는 그 메인 계정의 Reserve를 활성화할 수 없으므로, 메인 계정을 끝까지
쓰고 Reserve로 넘어가려면 스위치를 끄세요.

**5h 창이 있으면 5h 사용률**, 없으면 주간 사용률을 봅니다. 월간 전용 계정은 월간을
기준으로 합니다. 여러 창에서 가장 높은 값을 고르는 방식은 아닙니다. 새 사용률이 **0%**로
리셋되면 자동으로 차단을 풀고, 스위치는 켜 둡니다. 이후 다시 98%가 되면 차단합니다.
**5h 창과 주간 창은 각각 따로 차단합니다.** 둘 중 하나만 98%에 닿아도, 다른 창에 여유가
있어도 바로 차단합니다. 월간 전용 계정은 월간을 기준으로 합니다. 차단한 창이 모두 새 사용률
98% 미만(0% 리셋 포함)을 보고하면 자동으로 차단을 풀고, 스위치는 켜 둡니다. 이후 다시 98%가
되면 차단합니다. 5h 수치를 읽지 못해도 주간 차단은 그대로 유지됩니다.
값이 빠진 응답을 0%로 보지 않으며, 이미 확인한 차단 수치를 누락된 응답만으로 지우지도 않습니다.
예정된 리셋 시간이 지났다는 이유만으로 풀지는 않습니다. 차단 중에는 기존 1분 주기 점검에서
실제 사용량을 다시 확인하며, 조회 실패나 잘못된 수치는 차단을 풀지 않습니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,11 @@ account usable ([#5694](https://github.com/lidge-jun/opencodex/issues/5694)). Th
Reserve: while the block is in force, Reserve on that main account cannot activate. To let the main
account run to exhaustion and hand over to Reserve, turn the switch off.

The policy uses the **5h window when present**, otherwise the weekly window. Monthly-only
accounts use their monthly window. It does not take the highest percentage across windows.
A fresh **0%** observation automatically releases the block while the switch stays on; the next
98% observation blocks again. Unknown usage does not fabricate a zero, and a missing reading does
The **5h window and the weekly window each block on their own**: either one reaching 98% blocks
immediately, even while the other still has headroom. Monthly-only accounts use their monthly
window. The block releases automatically, with the switch still on, once every blocking window
reports a fresh reading below 98% (a 0% reset counts); the next 98% observation blocks again.
An unreadable 5h reading cannot hide a weekly block. Unknown usage does not fabricate a zero, and a missing reading does
not erase an already measured blocking tuple. A predicted reset time alone does not unlock it.
While blocked, the existing once-per-minute background cycle checks fresh owned usage; failed or
invalid readings retain the block. Other pause, reauthentication, and upstream limits remain independent.
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ export const de: Record<TKey, string> = {
"codexAuth.refreshQuota": "Kontingente aktualisieren",
"codexAuth.ultraFastTitle": "Ultra-Fast-Diensttarif",
"codexAuth.mainHardLockTitle": "Hauptkonto bei 98 % sperren",
"codexAuth.mainHardLockDesc": "Verwendet das 5-Stunden-Fenster, falls vorhanden, sonst das Wochenfenster (bei rein monatlichen Konten das Monatsfenster). Ein neuer Wert von 0 % hebt die Sperre automatisch auf; der Schutz bleibt aktiv. Standardmäßig aktiviert.",
"codexAuth.mainHardLockDesc": "Sperrt, sobald das 5-Stunden-Fenster oder das Wochenfenster 98 % erreicht (bei rein monatlichen Konten das Monatsfenster). Die Sperre hebt sich automatisch auf, wenn alle sperrenden Fenster unter 98 % liegen; der Schutz bleibt aktiv. Standardmäßig aktiviert.",
"codexAuth.mainHardLockConfirmTitle": "98-%-Schutz für das Hauptkonto aktivieren?",
"codexAuth.mainHardLockConfirmBody": "Während der Sperre ist auch Luna Reserve für das Hauptkonto nicht verfügbar. Ohne vollständigen Verbrauch des normalen Kontingents wird Reserve möglicherweise nicht aktiviert. Zusätzliche Konten und andere Anbieter bleiben nutzbar. Laufende Anfragen, nicht zugeordnete Schlüsselbund-Zugangsdaten und Anfragen außerhalb dieses Proxys sind nicht geschützt.",
"codexAuth.mainHardLockConfirm": "Schutz aktivieren",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2204,7 +2204,7 @@ export const en = {
"codexAuth.refreshQuota": "Refresh quotas",
"codexAuth.ultraFastTitle": "Ultra Fast service tier",
"codexAuth.mainHardLockTitle": "Block main account at 98%",
"codexAuth.mainHardLockDesc": "Uses 5h usage when available, otherwise weekly (monthly for monthly-only accounts). A fresh 0% reading unlocks automatically; protection stays on. On by default.",
"codexAuth.mainHardLockDesc": "Blocks when either the 5h or the weekly window reaches 98% (monthly for monthly-only accounts). Unlocks automatically once every blocking window reads below 98%; protection stays on. On by default.",
"codexAuth.mainHardLockConfirmTitle": "Enable the main account 98% lock?",
"codexAuth.mainHardLockConfirmBody": "While blocked, the main account cannot use Luna Reserve. Keeping normal usage below exhaustion may prevent Reserve activation. Added accounts and other providers remain available. Running requests, unmatched keyring credentials, and traffic outside this proxy are not protected.",
"codexAuth.mainHardLockConfirm": "Enable protection",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2136,7 +2136,7 @@ export const fr: Record<TKey, string> = {
"codexAuth.refreshQuota": "Actualiser les quotas",
"codexAuth.ultraFastTitle": "Niveau de service Ultra Fast",
"codexAuth.mainHardLockTitle": "Bloquer le compte principal à 98 %",
"codexAuth.mainHardLockDesc": "Utilise la fenêtre de 5 h si elle existe, sinon la semaine (le mois pour les comptes mensuels uniquement). Une nouvelle mesure à 0 % lève le blocage automatiquement ; la protection reste active. Activé par défaut.",
"codexAuth.mainHardLockDesc": "Bloque dès que la fenêtre de 5 h ou la fenêtre hebdomadaire atteint 98 % (le mois pour les comptes mensuels uniquement). Le blocage se lève automatiquement quand toutes les fenêtres bloquantes repassent sous 98 % ; la protection reste active. Activé par défaut.",
"codexAuth.mainHardLockConfirmTitle": "Activer la protection à 98 % du compte principal ?",
"codexAuth.mainHardLockConfirmBody": "Pendant le blocage, Luna Reserve est également indisponible sur le compte principal. Ne pas épuiser le quota normal peut empêcher l’activation de Reserve. Les comptes ajoutés et les autres fournisseurs restent utilisables. Les requêtes en cours, les identifiants du trousseau non reconnus et le trafic hors de ce proxy ne sont pas protégés.",
"codexAuth.mainHardLockConfirm": "Activer la protection",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2059,7 +2059,7 @@ export const ja: Record<TKey, string> = {
"codexAuth.refreshQuota": "クォータを更新",
"codexAuth.ultraFastTitle": "Ultra Fast サービスティア",
"codexAuth.mainHardLockTitle": "メインアカウントを98%で停止",
"codexAuth.mainHardLockDesc": "5時間枠があればその使用率、なければ週間使用率を使います(月間のみのアカウントは月間)。0%にリセットされると自動解除し、設定は有効のままです。既定でオンです。",
"codexAuth.mainHardLockDesc": "5時間枠と週間枠のどちらかが98%に達すると停止します(月間のみのアカウントは月間)。停止の原因になった枠がすべて98%未満になると自動解除し、設定は有効のままです。既定でオンです。",
"codexAuth.mainHardLockConfirmTitle": "メインアカウントの98%保護を有効にしますか?",
"codexAuth.mainHardLockConfirmBody": "停止中はメインアカウントのLuna Reserveも使えません。通常枠を使い切らない場合、Reserveが有効にならないことがあります。追加アカウントや他のプロバイダーは引き続き使えます。実行中のリクエスト、照合できないキーチェーン認証情報、このプロキシ外の通信は対象外です。",
"codexAuth.mainHardLockConfirm": "保護を有効にする",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1652,7 +1652,7 @@ export const ko: Record<TKey, string> = {
"codexAuth.refreshQuota": "할당량 새로고침",
"codexAuth.ultraFastTitle": "Ultra Fast 서비스 티어",
"codexAuth.mainHardLockTitle": "메인 계정 98% 차단",
"codexAuth.mainHardLockDesc": "5h 창이 있으면 5h, 없으면 주간 사용률을 기준으로 합니다. 월간 전용 계정은 월간을 봅니다. 0%로 리셋되면 자동으로 풀리고 설정은 유지됩니다. 기본값으로 켜져 있습니다.",
"codexAuth.mainHardLockDesc": "5h 창이나 주간 창 중 하나라도 98%에 닿으면 차단합니다. 월간 전용 계정은 월간을 봅니다. 차단한 창이 모두 98% 아래로 내려오면 자동으로 풀리고 설정은 유지됩니다. 기본값으로 켜져 있습니다.",
"codexAuth.mainHardLockConfirmTitle": "메인 계정 98% 차단을 켤까요?",
"codexAuth.mainHardLockConfirmBody": "차단 중에는 메인 계정의 Luna Reserve도 사용할 수 없습니다. 일반 사용량이 소진되지 않으면 Reserve가 활성화되지 않을 수 있습니다. 추가 계정과 다른 공급자는 계속 사용할 수 있습니다. 진행 중 요청, 식별되지 않은 키링 계정, 프록시 밖 요청에는 적용되지 않습니다.",
"codexAuth.mainHardLockConfirm": "확인하고 켜기",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2131,7 +2131,7 @@ export const ru: Record<TKey, string> = {
"codexAuth.refreshQuota": "Обновить квоты",
"codexAuth.ultraFastTitle": "Уровень обслуживания Ultra Fast",
"codexAuth.mainHardLockTitle": "Блокировать основной аккаунт при 98%",
"codexAuth.mainHardLockDesc": "Используется окно 5 ч, если оно есть, иначе недельное (месячное для аккаунтов только с месячным лимитом). Новое значение 0% автоматически снимает блокировку; защита остаётся включённой. Включено по умолчанию.",
"codexAuth.mainHardLockDesc": "Блокирует, когда окно 5 ч или недельное окно достигает 98% (месячное для аккаунтов только с месячным лимитом). Блокировка снимается автоматически, когда все блокирующие окна опускаются ниже 98%; защита остаётся включённой. Включено по умолчанию.",
"codexAuth.mainHardLockConfirmTitle": "Включить защиту основного аккаунта при 98%?",
"codexAuth.mainHardLockConfirmBody": "Во время блокировки Luna Reserve основного аккаунта тоже недоступна. Если обычная квота не исчерпана, Reserve может не активироваться. Дополнительные аккаунты и другие провайдеры остаются доступны. Текущие запросы, несопоставленные данные связки ключей и запросы вне этого прокси не защищены.",
"codexAuth.mainHardLockConfirm": "Включить защиту",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2161,7 +2161,7 @@ export const tr: Record<TKey, string> = {
"codexAuth.refreshQuota": "Kotaları yenile",
"codexAuth.ultraFastTitle": "Ultra Fast hizmet katmanı",
"codexAuth.mainHardLockTitle": "Ana hesabı %98’de durdur",
"codexAuth.mainHardLockDesc": "Varsa 5 saatlik, yoksa haftalık kullanım esas alınır (yalnızca aylık hesaplarda aylık kullanım). Yeni %0 ölçümü engeli otomatik kaldırır; koruma açık kalır. Varsayılan olarak açık.",
"codexAuth.mainHardLockDesc": "5 saatlik veya haftalık pencereden biri %98'e ulaştığında engeller (yalnızca aylık hesaplarda aylık kullanım). Engelleyen tüm pencereler %98'in altına indiğinde engel otomatik kalkar; koruma açık kalır. Varsayılan olarak açık.",
"codexAuth.mainHardLockConfirmTitle": "Ana hesap için %98 koruması açılsın mı?",
"codexAuth.mainHardLockConfirmBody": "Engel sürerken ana hesabın Luna Reserve erişimi de kullanılamaz. Normal kotanın tükenmemesi Reserve’in etkinleşmesini önleyebilir. Ek hesaplar ve diğer sağlayıcılar kullanılmaya devam eder. Çalışan istekler, eşleştirilemeyen anahtarlık kimlik bilgileri ve bu proxy dışındaki trafik korunmaz.",
"codexAuth.mainHardLockConfirm": "Korumayı aç",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,7 @@ export const vi: Record<TKey, string> = {
"codexAuth.refreshQuota": "Làm mới quota",
"codexAuth.ultraFastTitle": "Dịch vụ cấp độ Ultra Fast",
"codexAuth.mainHardLockTitle": "Khóa tài khoản chính ở mức 98%",
"codexAuth.mainHardLockDesc": "Sử dụng mức 5h nếu có, nếu không thì dùng hàng tuần (hoặc hàng tháng đối với tài khoản chỉ có hàng tháng). Một kết quả đọc 0% mới sẽ tự động mở khóa; bảo vệ vẫn tiếp tục bật. Mặc định bật.",
"codexAuth.mainHardLockDesc": "Chặn khi mức 5h hoặc mức hàng tuần chạm 98% (hàng tháng đối với tài khoản chỉ có hàng tháng). Tự động mở khóa khi mọi mức đang chặn đều xuống dưới 98%; bảo vệ vẫn tiếp tục bật. Mặc định bật.",
"codexAuth.mainHardLockConfirmTitle": "Bật khóa 98% tài khoản chính?",
"codexAuth.mainHardLockConfirmBody": "Khi bị khóa, tài khoản chính không thể sử dụng Luna Reserve. Giữ lượng dùng dưới mức cạn kiệt có thể ngăn việc kích hoạt Reserve. Các tài khoản được thêm vào và provider khác vẫn khả dụng. Các request đang chạy, credential chưa khớp, và lưu lượng ngoài proxy này không được bảo vệ.",
"codexAuth.mainHardLockConfirm": "Bật bảo vệ",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,7 @@ export const zhTW: Record<TKey, string> = {
"codexAuth.refreshQuota": "重新整理額度",
"codexAuth.ultraFastTitle": "Ultra Fast 服務層級",
"codexAuth.mainHardLockTitle": "主帳戶用量達 98% 時阻擋請求",
"codexAuth.mainHardLockDesc": "有 5 小時額度時以該額度為準,否則使用週額度(僅有月額度的帳戶使用月額度)。新用量重設為 0% 後會自動解除阻擋,保護設定仍保持開啟。預設開啟。",
"codexAuth.mainHardLockDesc": "5 小時額度或週額度任一達到 98% 即阻擋(僅有月額度的帳戶使用月額度)。所有觸發阻擋的額度都降到 98% 以下後自動解除,保護設定仍保持開啟。預設開啟。",
"codexAuth.mainHardLockConfirmTitle": "開啟主帳戶 98% 保護?",
"codexAuth.mainHardLockConfirmBody": "阻擋期間,主帳戶也無法使用 Luna Reserve。一般額度未用盡時,Reserve 可能不會啟用。新增帳戶與其他供應商仍可使用。進行中的請求、無法比對的鑰匙圈憑證,以及此代理之外的流量不受此保護。",
"codexAuth.mainHardLockConfirm": "開啟保護",
Expand Down
2 changes: 1 addition & 1 deletion gui/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,7 @@ export const zh: Record<TKey, string> = {
"codexAuth.refreshQuota": "刷新额度",
"codexAuth.ultraFastTitle": "Ultra Fast 服务层级",
"codexAuth.mainHardLockTitle": "主账户用量达 98% 时阻止请求",
"codexAuth.mainHardLockDesc": "有 5 小时额度时以该额度为准,否则使用周额度(仅有月额度的账户使用月额度)。新用量重置为 0% 后会自动解除阻止,保护设置仍保持开启。默认开启。",
"codexAuth.mainHardLockDesc": "5 小时额度或周额度任一达到 98% 即阻止(仅有月额度的账户使用月额度)。所有触发阻止的额度都降到 98% 以下后自动解除,保护设置仍保持开启。默认开启。",
"codexAuth.mainHardLockConfirmTitle": "开启主账户 98% 保护?",
"codexAuth.mainHardLockConfirmBody": "阻止期间,主账户也无法使用 Luna Reserve。普通额度未耗尽时,Reserve 可能不会激活。附加账户和其他提供商仍可使用。正在进行的请求、无法匹配的钥匙串凭据以及此代理之外的流量不受此保护。",
"codexAuth.mainHardLockConfirm": "开启保护",
Expand Down
Loading
Loading