## 账号池排障

### 客户可见错误与配置调优

1. 先用 `runtime errors --since <window>` 获取全账号同窗口证据：
   - 以 Sub2API 原生 dashboard overview 的请求分母、客户错误率和上游错误率为主。
   - 以 account availability、concurrency 和 upstream errors 解释账号可用性、排队、状态码与根因。
   - 内部 monitor 使用真实 API key 和正式入口产生的错误必须纳入正式错误率、账号归因、trace 和调优判断；只单独标注调用方类别，不得因其不是外部客户而剔除或降级为测试噪声。
   - 原生 system-log 索引缺少策略事件时，才使用 target runtime 日志补临时不可调度、failover、`account_select_failed` 和 `forward_failed`，并保留 fallback 披露。
2. 从客户可见错误中选有限 request id 运行 `trace`，按请求链区分：
   - 上游失败后成功切号。
   - 切号后候选耗尽。
   - 首次选号即无候选。
   - HTTP 200 已提交流式响应后出现 `forward_failed` 的 degraded 请求。
3. 规则证据必须同时包含真实 HTTP 状态码与响应体关键词：
   - 只有状态码、通用包装文案或推断出的原因不能扩展关键词。
   - 只有 `account_temp_unschedulable` 才证明规则命中；不能用相同状态码再次出现来推断规则失效。
   - 优先使用精确短语，避免把 `context window`、认证失败、模型不支持等不可互换错误混入通用不可用规则。
4. 无候选或模型路由错误使用现有正式只读入口：
   - `runtime get --account <selector>` 读取账号配置和调度状态；`runtime errors --account <selector> --full` 读取原生 Ops 已观测模型证据。
   - 配置声明与已观测模型必须分开报告，不能把历史请求模型误当作实时上游能力。
   - 没有账号真实支持目标模型时，保持池不声明该模型并快速失败，不映射到名称相近的其他模型。
   - 没有原生实时能力接口时只能标记证据不足，不能据此补映射。
5. 调优按所有权下发：
   - 非 auth 上游错误优先抽象为通用临时不可调度模板：
     - 规则同时使用真实状态码和已证实的稳定响应短语；
     - 先从当前 Sub2API 源码确认规则命中会在响应提交前触发当前请求 failover；
     - 禁止在模板、CLI 或源码中硬编码账号名称、账号 ID、provider URL 或分组特例；
     - 禁止用通用 400、通用 `invalid request` 或宽泛关键词覆盖真实客户参数错误；
   - auth、token revoked、余额不足、分组删除和凭据失效使用专门的认证与账号状态路径，不混入通用非 auth 模板；
   - 通用模板先修改 owning YAML，并保持冷却保守；默认从一分钟开始，没有新证据和明确授权不得超过三分钟。
   - YAML-managed 账号走 `sync`；runtime-manual 账号走显式 `runtime apply|delete`。
   - 多账号使用 `--accounts` 精准集合；例外账号从 selector 中显式排除，不使用隐式全量操作。
   - 先 dry-run 核对 change、before/after 和账号集合，再加 `--confirm`，最后逐账号 `get` 或批量 noop dry-run 对账。
6. 不把当前并发快照当作错误时并发：
   - 只有错误原文明确指向 concurrency，或存在错误时历史并发证据，才把降低上游并发作为有界对照实验。
   - 缺少直接证据时先处理主导上游根因。
7. 下发后立即拉短窗口只做回归检查：
   - 效果判定必须等待新的完整窗口。
   - 同时比较请求分母、客户错误率、上游错误率、规则命中、切号成功、候选耗尽和 degraded 流式请求。
8. 版本回退必须有源码差异或版本对照证据证明当前版本引入回归：
   - 旧版具有同一选择或错误传播逻辑时，不用回退替代配置治理。
   - 新版包含相关 failover 修复时，优先保留新版并治理真实根因。

- Codex pool 哨兵、账号冻结/恢复、marker-only 判断或 probe 周期看不清：第一步跑 `bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-report`。这个报表是主观察面；只有报表缺字段或需要底层证据时，才继续看 `--raw`、CronJob log、state ConfigMap 或 Sub2API 管理 UI。若看到“临时不可调度状态”且包含规则序号/匹配关键词，检查 Sub2API `account_temp_unschedulable` 日志和账号 `temp_unschedulable_*` 字段；sentinel 只解释 `schedulable=false` 的 active quarantine，不解释这类内置临时冷却。
- 只加强监控、不让哨兵自动冻结账号时，把 YAML `sentinel.actions.enabled=false` 后 `codex-pool sync --confirm`。此时 marker probe 和 gateway failure monitor 仍记录 `would-freeze` / observe-only 证据，但不会通过 Sub2API admin 写 `schedulable=false`；`/responses/compact` 的 `codex.remote_compact.failed` 和 compact 上游 5xx failover 只作为 `gateway-compact-*` 观察事件记录，不作为哨兵自动切换触发器。
- 单个 request id 报 502/503/中断/没有自动切号：第一步跑 `bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id <requestId>`。先看 `outcome`、`reason`、`FAILOVER`、`SELECT-FAILED`、`ACCOUNT SIGNALS` 和 `WINDOW STATS`；只有 trace 报表缺字段或需要审计原始日志时，才加 `--show-lines` 或 `--raw`。若 `reason=failover-attempted-no-candidate`，说明切号动作已发生，但 scheduler 在排除失败账号后没有可用候选；继续用 `sentinel-report` 和 `validate --full` 区分 sentinel quarantine、request-path temp-unschedulable、账号 status 或容量耗尽。
- profile invalid：先修 `~/.codex/config.toml.<profile>` 的 `base_url`、`wire_api`、`model` 或 `auth.json.<profile>` 的 API key；不要在 YAML 中写密钥。
- 手动 OAuth/API-key 账号的 WebUI account test 连 `chatgpt.com` 超时，但目标运行面显式 HTTP proxy 探针可通：不要只看 Pod 或容器环境变量，按“受保护手动账号代理与分组绑定”小节确认 `manualAccounts.protected[].proxyBinding`，跑 `codex-pool sync --target <id> --confirm` 后再用原账号测试复测。若复测不再 reset/timeout，而是 `gpt-5.2-pro` 这类指定模型返回 ChatGPT OAuth Codex 不支持的能力错误，用默认/受支持模型或统一 key smoke 验证代理，不要把模型错误当作代理仍坏。
- 手动 OAuth/API-key 账号 WebUI account test 正常，但 PC Codex 客户端通过统一 key 访问 `/responses` 返回 503 且 trace 是 `account-select-failed` / `no available accounts`：按“受保护手动账号代理与分组绑定”小节确认该账号已绑定统一 key 使用的 pool group。WebUI group 列表和账号详情不一定足以证明 scheduler 可调度；必要时核对 admin account availability 与 `account_groups` join。k3s target 和 PK01 host-Docker target 都通过 `codex-pool sync --target <id> --confirm` 后用 `codex-pool validate --target <id> --full` 复测统一 key；如果 `MANUAL=N` 指向受保护手动账号缺失，先按账号所有权恢复或退役该手动账号，不把它混同为 YAML-managed pool 账号失败。
- pool key 401：跑 `codex-pool sync --confirm` 重建 Sub2API key 与 k3s Secret 绑定，再跑 `codex-pool validate`。
- pool key、admin password 或 k8s Secret `.data` 被 stdout、日志、issue 或本地 transcript 打印时，按泄露处理：撤销对应 Sub2API key 或 token，删除/重建受影响的 target Secret，通过 `codex-pool sync --target <id> --confirm` 或相应 YAML sourceRef 重新下发，再用 fingerprint、presence 和 `valuesPrinted=false` 作为 closeout 证据；不要复述旧值或新值。
- 运行中过去的验证探针残留：只用 `codex-pool cleanup-probes --confirm` 清理 `unidesk-probe-*` 临时资源；不要把真实 managed account 删除当作探针清理或可用性恢复。
- default profile 递归：检查 YAML default entry 是否使用 `*.pre-sub2api` 备份文件；必要时恢复备份后重新 `configure-local --confirm`。
- 上游需要 WebSocket v2：先做 direct Codex WSv2 probe；通过后才给该 profile 配 `openaiResponsesWebSocketsV2Mode: ctx_pool|passthrough` 并跑 `sync --confirm`；把它当 capability candidate，容量仍以 YAML 中的 `capacity` 或默认值为准。
- Codex 启动 WebSocket 回退：用原入口 Codex smoke 复现，再用 bounded Sub2API 日志确认 account；对 WS handshake 4xx/5xx、`openai.websocket_account_select_failed` 或 close-before-`response.completed` 的账号关闭 YAML WSv2 能力后同步。若没有剩余 WSv2-capable account，把 `localCodex.supportsWebSockets` 和 `localCodex.responsesWebSocketsV2` 一起关掉，不把临时可用性推断写成调度配置。
- 上游要求 Codex User-Agent：只给该 profile 配 `upstreamUserAgent`，跑 `sync --confirm`。
- 上游报 capacity/rate-limit/overload/Bad Gateway/Gateway Timeout 后没有隔离或频繁先失败再恢复：先看 `codex-pool sentinel-report` 的 marker、动作、冻结 TTL 和下一次 probe，也看 `codex-pool validate --full` 的 recent gateway failover/forward failure 证据；同时对照当前 Sub2API 源码里 `/v1/responses` handler、`Forward`、`shouldFailoverOpenAIUpstreamResponse` 和 `handleOpenAIAccountUpstreamError` 的真实传播路径。不要手动禁用账号、删除账号、改 membership/priority/capacity/loadFactor 或从 YAML 移除问题账号来替代通用 failover 与哨兵隔离/恢复。
- Codex 报 weekly-limit、`less than 10% of your weekly limit left`、`Run /status for a breakdown` 等账号状态/软配额提示并要求切号：不要把新关键词写成 Sub2API 内置临时不可调度策略来恢复可用性；由 marker-only 哨兵按非 marker 响应统一冻结，并用 `sentinel-report` / `sentinel-probe` 验证。
- 上游 400/503 响应体出现 `invalid_encrypted_content`、`bad_response_status_code`、`invalid_request_error` + 稳定 unsupported-model 文案、unsupported-model、`暂不支持` / `可用模型`、`model_not_found`、`No available channel for model ...` 或同类稳定模型路由 / Responses encrypted-content 兼容性失败：按通用 temp-unschedulable/failover 加哨兵 marker 证据处理，不用 account membership、priority、capacity、loadFactor、WebSocket mode、User-Agent 或 provider pinning 掩盖该错误族。
- 上游错误反复触发：`invalid_encrypted_content`、unsupported-model、`Recovered upstream error ...`、`Bad Gateway`、`Gateway Timeout`、Cloudflare `524`、Codex-facing `Upstream request failed`、`Unknown error`、`context deadline exceeded`、`context canceled`、`model_not_found`、`No available channel for model`、大上下文 `413` 和 `openai_error` 这类稳定包装文案，先确认 YAML temp-unschedulable 已同步、Sub2API 源码会把该错误族传播成 `UpstreamFailoverError`、运行日志出现 `openai.upstream_failover_switching`。若匹配规则后仍只看到 `openai.forward_failed`，根因是 Sub2API HTTP `/responses` 没把该错误传播成 `UpstreamFailoverError`，应修 Sub2API failover classifier/error propagation，不硬编码账号或给 `only` 特权。
- Codex auto compact 后丢上下文：先确认 YAML `localCodex` 是否声明启用 WSv2；若启用，再确认本机 `~/.codex/config.toml` 是否有 `supports_websockets = true` 和 `responses_websockets_v2 = true`，并看 `codex-pool validate` 的 WSv2 candidate 和 Sub2API 日志里的 `transport=responses_websockets_v2`。若 YAML 当前禁用 WSv2，则按 HTTP Responses 稳定性排查，不把旧 WS 口径当成验收要求。
- Codex smoke 有 reconnect/1013：这是上游并发/可用性问题，和 HTTP-only compact context-loss 分开处理；记录 session/log 证据并关联专项 issue，不要用运行时手补覆盖 YAML 容量。
