创建时间:2026-07-17 最后更新:2026-08-02 状态:🚧 最终 route/DB 修复已落地:GPT-5.6 与 Kimi capability 可到达 composer,智谱 CodePlan 无网络时回退内置目录,Claude selector 复用模型菜单视觉合同。2026-07-28 已把 Opus 5 作为显式模型接入 Claude Code/Native/Codex proxy 共用目录、Sub-agent route、1M context 与 adaptive/effort 合同;effort 现保留用户选择/系统兼容兜底 provenance,并按模型×档位在 wire 边界校验。2026-08-02 新增 DeepSeek V4 Flash 0731 slice:模型 ID 保持
deepseek-v4-flash,第一方 Flash 在 Codex Runtime 走原生 Responses,CodePilot/Claude Code 保持 Anthropic-compatible;Flash Low/High/Max、Pro High/Max 能力与 wire 分轴,ClinePass/OpenCode Go 不继承第一方 effort。两条真实 API smoke 已通过。packaged UI、Claude Code 子进程与 Kimi/GLM 等其余 provider smoke 仍待跑,Phase 3–4 继续进行。 事实基线:基础体验更新事实基线
用户需要 GLM-5.2、GPT-5.6(Codex)、Kimi for Coding 最新模型渠道和 Claude 新模型都能选择真实支持的推理强度,并希望 Claude 的控件出现在模型右侧。
仓库已经有该位置和控件,问题是 capability 真源不完整:GLM/Kimi 目录过期,Sonnet 5 缺失,Codex 同时存在动态与硬编码目录,而且 app-server 字段已从 effort 漂移为 reasoningEffort。本计划不重做 UI,而是把目录、能力、wire 参数和失败降级收敛成同一合同。
| Phase | 内容 | 状态 | 用户能看到什么 |
|---|---|---|---|
| Phase 0 | Codex GPT-5.6 与 schema drift | ✅ 恢复修复:最终序列化统一 lift capability;route-contract 直接断言 GPT-5.6 top-level effort allowlist | Codex 渠道可看到账号真实返回的 5.6,并显示可用强度 |
| Phase 1 | GLM-5.2 / Kimi for Coding 目录与强度 | ✅ 恢复修复:manual exact-ID + hidden legacy alias 仍可只读 enrichment;CodePlan search 以内置目录降级 | 两个 Coding Plan 显示正确模型和真实档位 |
| Phase 2 | Claude Sonnet 5 / Opus 5 与现有模型复核 | ✅ 恢复修复:Opus 5 显式目录、1M context、三 Runtime 共用 adaptive/effort、effort provenance/精确档位门与本地化提示已落地;真实 CLI route/entitlement 通过,packaged UI smoke 待跑 | Sonnet 5 / Opus 5 可选;Claude 模型右侧稳定显示匹配且视觉一致的强度菜单 |
| Phase 3 | capability 统一与后续跟进机制 | 🔄 进行中:DeepSeek model/UI 与 verified wire 已分轴,通用 normalization 仍待收口 | 上游模型变化不会再靠多处硬编码静默漂移 |
| Phase 4 | Tier 2 回归与真实凭据 smoke | 🔄 进行中:DeepSeek Responses + Anthropic 两条真实 API 通过,其余矩阵待跑 | 模型、Runtime、强度和实际请求一致 |
- GPT-5.6 不是 app-server 能力缺失。 当前 route 返回的 GPT-5.6 行在 nested
capabilities中已有supportsEffort=true与完整档位,但 Codex virtual group 是在 DB provider 的 lift pass 之后才追加;MessageInput只读 top-level 字段。修复应统一最终输出 schema,不能在 UI 再加 Codex 特判。 - Kimi 不是只补一个布尔值。 实际启用的
kimi-for-coding是manual_enabled/user_edited行,capabilities 为空;旧sonnetcatalog 行被manual_hidden,所以 catalog round-trip 测试没有覆盖真实用户路径。用户可见名称继续固定Kimi for Coding,wire 继续使用渠道 ID;2026-07-20 K3 文档与用户现场确认共同更新 capability 为 low/high/max,但 UI 不展示底层版本名。 - CodePlan model search 不应成为主路径。 本次 GLM 与 Kimi 的 search route 都在 8 秒超时;直连探测显示当前开发机没有 DNS。即使恢复网络,仓库自己的 discovery 研究也把 Coding Plan 定义为套餐白名单,官方文档没有承诺
/v1/models长期可用。 - Claude 视觉问题是真实但范围精确。 两个 trigger 的高度、padding、外层圆角已相同;不一致来自 model label 使用 mono、effort label 使用 sans,以及 effort popover 覆盖为
rounded-lg、独立窄宽度、缺 model popover 动画。不要重做整个 composer。
- 在最终 provider-model serialization 建立单一 capability normalization pass,覆盖 DB provider、env、OAuth、Codex virtual group;
MessageInput只消费一种 schema。 - 增加 route-contract 行为测试:真实调用
/api/providers/models的组装出口,断言 GPT-5.6 top-level effort fields 可读;builder-only 测试不再作为 UI 证据。 - Kimi manual exact-ID 行使用真实上游 ID 作为 canonical identity;旧
sonnet只保留存量解析兼容。只读 enrichment 合并 catalog 默认能力,但 DB 明确字段优先且不写回/覆盖用户数据。 - 按用户决定只显示
Kimi for Coding,wire 固定使用该产品渠道自己的kimi-for-codingID;不展示k3,也不增加按底层版本切换的兼容分支。当前 capability 合同为 Auto(不下发)+ Low/High/Max。 - ClinePass 与 OpenCode Go 的套餐目录新增显式 Kimi K3(分别为
cline-pass/kimi-k3与kimi-k3)。这不改变上一条:聚合套餐显式 SKU 与 Kimi 自有的Kimi for Codinglatest 渠道是不同产品语义。OpenCode ID/协议由官方 endpoint 表确认;ClinePass ID 由官方provider/model-name合同 + HTTP 200 最小真实请求确认。两个网关的 effort 请求字段尚未验证,故只声明 tool use,不在 composer 展示推理强度。 - GLM/Kimi CodePlan 的“添加模型”以当前 catalog + 手动 ID 为主;catalog-only 套餐不依赖可选
/models网络端点,不把 timeout 冒充“模型不存在”。 -
EffortSelectorDropdown复用模型选择器的 trigger typography 和CommandListgeometry/animation contract;mapping note 只扩内容宽度,不另造圆角/间距体系。 - DNS 恢复后重新跑 GPT-5.6、GLM-5.2、Kimi、Claude Sonnet/Fable 的 UI + wire smoke;本轮 ❌ 记录不得用 unit test 关闭。
- 不把 OpenAI API 目录硬塞给没有 entitlement 的 Codex Account。
- 不把
ultra当成普通 Responses API reasoning effort。 - 不继续用全局 clamp 把模型明确支持的 max/xhigh 静默降成 high。
- 先区分 UI 中
openai-oauth与codex_account,确定旧 OAuth 是否继续存在;若保留,必须明确命名与能力边界。部分:已做边界澄清(route.ts:16-31注释写明 openai-oauth = 静态手维护目录 / effort 服务端固定 medium / 禁止手加 5.6,codex_account = app-server 动态真源);本轮按裁决不删除 openai-oauth,是否更深收敛(合并/下线)已在 artifact judgment 提方案交 Codex 裁决。 -
model/list同时兼容旧{ effort }与新{ reasoningEffort },过滤空/未知值,保留 default effort 和描述。(models.ts:normalizeEffortElement;新字段优先、未知 token fail-closed 丢弃、default 不在解析结果内则清空) - 移除
src/components/chat/EffortSelectorDropdown.tsx:36的五档硬编码回退(伪档位来源):supportedEffortLevels缺失或为空时隐藏/降级选择器,不回退写死全集;组件层补空值、未知值、缺失 levels 的测试。(规则抽到src/lib/effort-levels.ts:resolveEffortMenuLevels直接单测,另加源码钉防回退复活) - Codex Account 目录以 app-server 为真源;版本低于 GPT-5.6 要求或账号未 rollout 时显示原因,不伪造条目。部分:空 model/list / 超时 / 未登录一律降级为「无 Codex 分组」且不伪造条目(有回归);「显示原因」沿用既有
/api/codex/status卡片,本轮未新增文案。 - 将 effort 校验改为“当前模型返回的 allowlist”;turn/start 只透传被该模型声明支持的档位。(
effort.ts:resolveCodexEffort+runtime.ts:938;模型声明的 xhigh/max 原样透传,未声明档位 omit 不外发,无 capability 信息时退回保守clampCodexEffort) - 将
ultra建模为 Codex 专属能力/模式,未完成多代理语义前不在通用 effort selector 中承诺。(CODEX_GENERIC_EXCLUDED_EFFORTS+toGenericEffortLevels:honest 解析、不进通用菜单) - 为 cache invalidation、旧 binary、logged-out、空 model/list、schema drift 加回归。(cache invalidation 已从 helper 级补到生产接线级:
account-transition.ts包住 logout / login start,两条 route 只经它调用;codex-account-cache-invalidation.test.ts覆盖 warm cache → logout/换号 →buildCodexProviderModelGroup({cacheOnly:true}) === null+getCachedCodexEffortLevels === undefined,并钉住 route 的接线)
- 不把供应商映射后的多个 UI 档位说成不同的实际推理计算。
- 不展示或跟踪 K3 等底层模型版本;
Kimi for Coding就是用户可见的最新模型渠道。 - 不新增显式
k3内置模型项,也不为供应商切换底层版本维护兼容分支。 - Kimi 当前支持 low/high/max;Auto 仍只是 CodePilot 不显式下发 effort 的产品语义,不冒充供应商档位。
- GLM CN/Global 更新 role mapping、默认模型与 1M 变体策略;在真实凭据前保留待验证标记。部分:目录升到 GLM-5.2 世代、sonnet/opus role env 均指向
glm-5.2(haiku 维持glm-4.5-air,基线未称其过期);[1m]变体本轮未加——基线把它列为待凭据验证项,无凭据前加变体等于凭空声明能力,已在 Smoke Ledger 留行。 - GLM capability 只暴露 high/max 的有效语义,必要时在菜单说明 Claude Code 档位映射。(
GLM_CODING_PLAN_MODELS:supportedEffortLevels: ['high','max']+effortNoteKey;菜单渲染 Auto/High/Max 并附映射说明,i18n en/zh 同步。审查轮 #1 修复(292bcce):原实现只在 catalog 对象上成立——db.ts两条 sync 路径把capabilities_json硬写'{}',DB 行一经 materialize 即遮蔽 catalog,菜单在真实路径上不出现;现 seed/align 均带 capabilities,round-trip 测试覆盖。映射文案补齐ultracode,与「六档折两档」注释一致) - Kimi 默认请求使用产品渠道 ID
kimi-for-coding,并给现有sonnetUI alias 补明确 upstream mapping;用户可见名称固定为Kimi for Coding。这是一项产品/wire 决策,不再写成“官方保证其等于当前 K3”的事实。(upstreamModelId: 'kimi-for-coding';modelId 保留sonnet以兼容存量;manual exact-ID 行在最终 read path 只读 enrichment。) - Kimi for Coding 内置目录不展示 discovery 返回的底层版本或显式
k3条目;供应商升级底层模型时不需要修改目录。(单测断言 preset 用户可见字段无K2.5/K3,且无 k3 条目) - capability 与模型展示名解耦:
supportedEffortLevels: ['low','high','max'],菜单显示 Auto/Low/High/Max。Auto 是 CodePilot 的“不显式指定”语义,不是 Kimi 官方档位;K3 最新文档明确支持三档,用户现场确认 Kimi for Coding 当前返回 K3,因此渠道目录同步扩档但仍只展示渠道名。effortNoteKey明确“Auto 不下发、由 Kimi 决定”。Low/High/Max 的真实/coding/接受情况仍须逐档 smoke,不把静态 capability 冒充 wire 通过。 - 验证 Kimi effort 下发链路与优先级:Agent SDK
queryOptions.effort与CLAUDE_CODE_EFFORT_LEVELenv override 的关系及 Kimi 渠道兼容性,不预设 env-only;effort 被上游忽略或报错时按能力漂移降级并给出提示,作为静态 catalog 声明的防线。未做(需真实凭据):本轮不动 env 注入代码,结论见决策日志p1-effort-chain段与 Smoke Ledger。 - Moonshot provider(
provider-catalog.ts:638-660)不属于本轮 Kimi for Coding 改名范围;但改动 catalog 时确认legacy-catalog-hint.test.ts:119-196(pin 了kimi-k2.5)不被破坏。(Moonshot 零改动 + 新增反向断言钉住其不改名;legacy-catalog-hint.test.ts未改动即全绿——该文件 pin 的是「用户手加kimi-k2.5不该报 legacy badge」,与 Kimi 内置目录改名正交) - 模型切换导致缓存失效时给用户可理解提示;不在同一 session 偷换模型。未做:转 Phase 2「模型切换时若旧档位不受支持,回到 Auto 并显示一次非误导提示」一并实现,避免两处各写一套提示。
- 不只在 catalog 加一行。
- 不把 manual extended thinking、非默认 sampling 参数继续发给会返回 400 的 Sonnet 5。
- 不自动把所有既有对话从 Sonnet 4.6 升级到 Sonnet 5。
- 不把既有
opusalias 从 4.7 静默改到 5;Opus 5 使用显式opus-5。 - 不给未经验证的 OpenRouter/Bedrock/Vertex 添加 Opus 5 slug,也不把 catalog 命中冒充账号 entitlement。
-
在 first-party、env、适用的 OpenRouter/Bedrock/Vertex 目录分别按真实可用性加入 Sonnet 5。(部分:first-party
ANTHROPIC_FIRST_PARTY_MODELS加sonnet-5→claude-sonnet-5,env/route/resolver/client fallback 经ENV_CLAUDE_CODE_MODELS自动派生;OpenRouter/Bedrock/Vertex slug 未验证不加——沿用 fable 纪律,sonnet-5-model.test.tspin 住「OpenRouter 无 sonnet-5」) -
更新 context、adaptive-thinking sanitizer、sampling 约束、token budget 与 provider capability。(
model-context.ts加claude-sonnet-5:1M + tokenizer +30% 注记;claude-model-options.ts新SONNET_5_PATTERN并入 adaptive 家族。sampling 约束(复审轮 #1 升级):不再靠「by construction 无可剥离」的注释,改为主动 guard——ClaudeModelOptionsInput收temperature/topP/topK,adaptive 家族非默认值(temperature≠1、任何显式 top_p/top_k)从sampling剥离并记入strippedSamplingParams告知信号(同thinkingForcedOn的 surface-don't-swallow 规则);非 adaptive(sonnet-4-6)原样透传不误伤;sonnet-5-model.test.ts覆盖剥离/放行/默认 temperature/4.6 不误伤/家族一致全分支。复审轮 #4 修复(run i31):Codex 指strippedSamplingParams在生产代码零消费者、两条 Runtime 也从未把真实 sampling 传进 sanitizer,剥离对用户完全静默,违反 sanitizer 自己声明的 surface-don't-swallow。改法:AgentLoopOptions/ClaudeStreamOptions/RuntimeStreamOptions加temperature/topP/topK并逐层透传进两条 Runtime 的 sanitizer 调用;新增共享决策模块anthropic-sampling-notice.ts:buildSamplingIgnoredNotice,两条 Runtime 都消费它并发一次SAMPLING_PARAMS_IGNORED状态通知(已加入TOAST_STATUS_CODES白名单,否则 toast 会被下一条 status 顶掉);native 额外把存活的sanitized.samplingspread 进streamText(省略即与改前逐字节一致),SDK 路径因query()无 sampling 旋钮,剥离与存活参数一并如实告知。新anthropic-sampling-notice.test.ts13 例断言通知真的发生(含反例:temperature=1 / 无参数 / 非 adaptive 模型不误报)+ 两条 Runtime 接线 pin) -
复核 Fable 5 / Opus 4.8 已有实现与当前官方合同是否一致。(无回归:
fable-5-model.test.ts/opus-4-8-sonnet-4-6.test.ts全绿;sonnet-5判定不误伤sonnet-4-6(非 adaptive),有专测) -
显式裁决
src/lib/agent-loop.ts:408-425(及agent-loop-toolloop-poc.ts)Native 路径丢弃显式 effort 的旧逻辑。裁决=恢复下发:核实@ai-sdk/anthropic@4.0.5走 GAoutput_config.effort,dist 内无effort-2025-11-24/ 任何 effort beta header(grep 0 命中),旧 workaround 前提失效。两条 native 路径改为对所有模型透传 effort,官方路径去掉RUNTIME_EFFORT_IGNOREDtoast(第三方代理路径保留),UI 选择 = wire 一致。复审轮 #1 升级:wire 构造抽到agent-loop-anthropic-wire.ts(buildAnthropicProviderOptions,随agent-loop-error-event.ts先例),agent-loop-anthropic-wire.test.ts以可执行行为断言 sonnet-5+xhigh 官方路径providerOptions.anthropic.effort='xhigh'且effortDroppedForProxy=false、代理路径丢弃并升起 drop 信号——从源码 pin 升级为真实对象断言。复审轮 #4 修复(run i31):Codex 独立复现claude-haiku-4-5-20251001+max在官方 Native 路径仍发出{"effort":"max"}——buildAnthropicProviderOptions根本不接收 model,「不再对 adaptive 家族丢弃」被实现成了「对所有模型无条件透传」,而官方 effort supported-model 列表不含 Haiku 4.5。改法:claude-model-options.ts新增带官方 breadcrumb 的 per-model allowlistANTHROPIC_API_EFFORT_MODELS+anthropicApiSupportsEffort()(刻意不派生自 catalogsupportedEffortLevels——那是 UI picker / Claude Code CLI 的能力面,更宽,first-party haiku 在那里声明 low/medium/high;用它当 wire 门就等于复活本 finding),wire 层改收model并只对 allowlist 内模型下发 effort,不支持/未知模型省略并升起新信号effortDroppedUnsupportedModel,两条 native 路径(agent-loop.ts+agent-loop-toolloop-poc.ts,后者本轮改为消费同一 helper 消除 drift)据此发一次RUNTIME_EFFORT_IGNORED(文案如实说明「该模型不支持 effort、按自身默认深度运行」,与真实行为不矛盾)。agent-loop-anthropic-wire.test.ts补三类可执行请求形状断言:Haiku 4.5+max → wire 无 effort 键 + 信号 true、未知模型同样 fail-closed、Sonnet 5+xhigh 正例仍effort='xhigh'且两个 drop 信号均 false;另加「allowlist 不得读 catalog / 每条必须有 breadcrumb」的守卫。fable-5-model.test.ts原钉住「官方分支无条件透传」的断言随之改为钉 per-model 门(旧断言前提被本轮推翻)。 -
触点补齐:
useProviderModels.ts客户端 fallback、route.tsENV_ALIAS_TO_UPSTREAM(均经ENV_CLAUDE_CODE_MODELS派生,自动含 sonnet-5,env-models-single-source.test.tspin)、i18nen/zh新增messageInput.effort.resetOnModelSwitch;token budget tokenizer +30% 注记进model-context.ts+ sanitizer。 -
保持强度控件紧邻模型选择器;模型切换时若旧档位不受支持,回到 Auto 并显示一次非误导提示。(新纯函数
resolveEffortAfterModelSwitch+ChatView.handleProviderModelChange接线;i18n 提示;effort-menu-levels.test.ts覆盖。复审轮 #1 升级:resolveModelSwitchEffortEffect(currentEffort, levels)→{resetEffort, showResetToast}把 reset/toast 决策收成可执行函数,ChatView 消费其结果。复审轮 #2 修复(run i31):Codex 指原实现 manual-only(isAuto 自动纠正与新会话chat/page.tsx入口均不清除不受支持的瞬态档位 → 切到不支持该档的模型后 UI 仍显示并发送该档,违反「UI 所选 = 发送参数 = 供应商档位」一致)。改法:resolveModelSwitchEffortEffect去掉 isAuto 参数——清除非法瞬态 effort 与「是否持久化 session pin」是两件事,前者对 manual 与 auto-correct 一致执行(isAuto 仅在调用点 gate 持久化 early-return),后者不变;chat/page.tsx新会话入口补接同一 helper + 一次性 sourced toast;MessageInput加emitProviderModelChange包装器,从与 picker 同一providerGroups/modelOptionsfeed 解析新模型supportedEffortLevels并经opts下发给两入口(同一真源校验),手动 picker 与 auto-correct 两路径都经该包装器。effort-menu-levels.test.ts重写为断言 manual/auto × 支持/不支持一致清除 + 清除后toWireEffort不下发被清档位 + 两入口 source pin(helper 调用先于 isAuto persist-skip)+ MessageInput feed 下发;codex-phase-6-wiring.test.ts的 isAuto 钉改为断言包装器...opts转发保 isAuto)复审轮 #3 修复(run i31):Codex 指复审轮 #2 只让父层setSelectedEffort(undefined),但MessageInput.tsx的selectedEffort = effortProp ?? localEffort会在父值清成 undefined 后回退到 stalelocalEffort——用户先选 xhigh、再切不支持 xhigh 的模型,按钮仍显示 xhigh 而 wire 已省略 effort,实际组件从未真正回到 Auto,仍违反「UI 所选 = 发送参数 = 供应商档位」;且原测试只测纯函数 + 源码顺序,未经过 MessageInput 显示解析。改法:显示解析抽成纯函数resolveComposerEffortDisplay(controlledEffort, localEffort, isControlled)——父层拥有 effort 时(onEffortChange已接,三个调用点都接)为真正受控,显示 =effortProp ?? 'auto',永不回退 stale local;localEffort仅供无onEffortChange的独立用法。effort-menu-levels.test.ts新增全链行为测试(makeComposerHarness用真实 helper 串起显示/reset/wire):两入口 × manual/auto-correct 先选 xhigh 再切 Sonnet 4.6 → 断言按钮 Auto、wire 省略 effort、toast 恰好一次;含 stale-local 回归守卫与「受控值压过 local」断言;MessageInput 源码钉住调用受控解析、effortProp ?? localEffort不复活) -
Opus 5 以显式
opus-5 → claude-opus-5加入 first-party/env 单一目录,并自动进入 Claude managed Sub-agent route;旧opus → claude-opus-4-7pin 保持不变,避免旧会话静默换模型。OpenRouter/Bedrock/Vertex 未验证 slug 保持缺席。 -
Opus 5 按官方合同加入 1M context、adaptive thinking、low/medium/high/xhigh/max effort 与 sampling guard;
thinking=disabled同时选择 xhigh/max 会在 Native/Claude Code 两条可关闭 thinking 的 Runtime 下调到 high,并以RUNTIME_EFFORT_ADJUSTED中英文提示用户;Auto 也显式发 high,不依赖 CLI 可变默认值。 -
Codex proxy 不再把 Anthropic effort 一律翻译成 manual
budgetTokens;它把解析后的 upstream model 交给同一 sanitizer/wire builder,Opus 4.7/4.8、Fable 5、Sonnet 5、Opus 5 均发送 adaptive + effort。xhigh已补进 proxy request type,第三方代理也不会再收到 adaptive 家族明确拒绝的 manual thinking 形状。 -
Claude Code 渠道基线核验:本机系统 CLI
2.1.220,官方从2.1.219加入 Opus 5;CodePilot 生产路径显式使用系统 CLI,故本轮不把 Agent SDK0.2.111 → 0.3.x大版本迁移夹带进模型目录修复。 -
Claude 复审收尾:sanitizer 新增
effortProvenance,区分用户显式档位与 Opus 5Auto + thinking off生成的兼容 High;第三方代理只对真实用户选择发RUNTIME_EFFORT_IGNORED,并保留调整前的原始档位。官方 Anthropic allowlist 从“模型布尔支持”升级为“模型 × 精确档位”,Sonnet 4.6 的xhigh在 Native/Codex proxy wire 边界均被省略,Native 以结构化中英文提示列出真实可用档位;第三方 proxy 提示也从服务端英文改为 code/reason/params 本地化。 -
Claude Code SDK 动态目录不得覆盖 canonical env 目录:
supportedModels()只作为补充入口,不能删除已成功路由的显式opus-5 / sonnet-5 / opus-4-8。身份重叠时 canonical 行优先,避免移动 alias 的描述把固定版本静默迁移;env-models-single-source.test.ts用真实 2.1.220 五行缓存复现并锁住“回复后仍显示 Opus 5”。 -
Phase 2 目标快照(s11 三处一致锚点):commit
fb53dfe(= 复审轮 #6 修复后 HEAD,含 effort allowlist 事实修正 + 运行时提示本地化;历史链a7c6795→03ca9b0→65a71ab→aa2623e→eb75df8);canonical 账本按命令语义分两栏(两者不得互相冒充):- 该次并行观测值(canonical 原样命令,计数随 force-exit 竞态逐次浮动):
# tests 4268 / # suites 1050 / # pass 4268 / # fail 0(exit 0)——Codex reviewer 在该 HEAD、全新空 HOME、前台原样执行 canonical 命令单次所得真实 footer;是一次真实门禁观测,不代表完整注册量。 - 完整注册量对照(
--test-concurrency=1,非 canonical 原样命令):# tests 4289 / # suites 1055 / # pass 4289 / # fail 0(exit 0)——同环境仅追加--test-concurrency=1的确定性串行对照,消掉 force-exit / worker flush 竞态后的完整注册量。
此前记录的
fb53dfe+ 4265/1049、03ca9b0+ 4241/1045、a7c6795+ 4235/1042 均为旧并行观测(4265 曾被误记为「未欠计数 / 唯一有效完整值」,该定性已撤回),演变过程保留在决策日志与权限计划账本中。本行(执行清单)/ 状态总览表 + frontmatter / 决策日志四处语义一致,详见决策日志复审轮 #8。 - 该次并行观测值(canonical 原样命令,计数随 force-exit 竞态逐次浮动):
- 建立 provider/model capability normalization:
supportedEffortLevels、default、thinking mode、context、source breadcrumb。 - DeepSeek V4 Flash 0731 更新保持稳定模型 ID;补 Flash Low/High/Max + 1M/default High、Pro High/Max,并以官方链接记录 source breadcrumb。
- 模型/UI capability 与 provider wire capability 分轴:preset
wireCapabilities经 identity + exact model 解析;DeepSeek 第一方 Flash 才有 Codex Responses,ClinePass/OpenCode Go 同名模型保持 tool-use-only。 - preset env 改为
catalog defaults < stored override分层读取;旧 DeepSeek provider 自动获得 Flash Sub-agent 默认值,用户 override 仍优先。 - API/SDK 动态能力优先;catalog 只作有 provenance 的 fallback。
- 增加 upstream schema fixture / contract test,字段改名或出现空值时 fail closed。
- 将模型目录复核列入 provider/runtime guardrail,新增模型必须同时回答 UI、wire、default、unsupported 四项。
- 单测:selector 可见性、档位集合、Auto 语义、模型切换清理、未知档位 fail-closed(含 EffortSelectorDropdown 空/未知/缺失 levels 的组件测试,禁止回退写死全集)。
- 单测:Codex 新旧
model/listschema;5.6 max/xhigh 不被静默降级;unsupported 不外发。 - 单测:Sonnet 5 / Opus 5 不发 manual thinking / 非默认 sampling;Opus 5 disabled-thinking × Auto/xhigh/max 生成合法 high wire;Codex proxy 全 adaptive 家族无 manual budget;Kimi for Coding 恰为 Auto/Low/High/Max;GLM 只表达两档真值。
- 单测:DeepSeek exact preset/model dispatch、legacy provider env 分层、Flash Codex Responses outbound body、Anthropic
output_config.effort、Max 保真 / xhigh→High / unsupported omit、aggregator fail-closed。 -
npm run test等价的权威串行全量:4410/4410,exit 0;默认并发脚本断言结束后受既有句柄影响不退出,因此完成证据采用--test-concurrency=1 --test-force-exit。 - 真实 smoke:Claude Code × GLM/Kimi/Anthropic;Codex Runtime × Codex Account;Native × Anthropic/OpenAI-compatible。
- DeepSeek 第一方真实 smoke:Codex Runtime × Flash × Responses High;CodePilot Runtime × Flash × Anthropic thinking + High。两条均返回预期 marker,且 production resolver/factory/wire builder 全链参与。
- 每个 smoke 记录 Runtime / Provider / Model / UI 选择 / wire 参数 / 实际结果。
- 用户在模型右侧看到的每个档位都有官方或运行时 source breadcrumb。
- UI 所选、session 持久化、发送参数、供应商实际档位四者一致;映射必须显式说明。
- 模型不支持或能力未知时隐藏/降级,不显示假选项。
- GPT-5.6、GLM-5.2、Kimi for Coding、Sonnet 5 / Opus 5 各有至少一个真实凭据 smoke;route/entitlement 与完整输出必须分开记账。
- 新模型不会改变旧会话已固定的 provider/model。
| Date | Runtime | Provider | Model | 凭据形态 | 场景 | Result | Evidence |
|---|---|---|---|---|---|---|---|
| 2026-08-02 | codex_runtime | DeepSeek official | deepseek-v4-flash | 已配置 API key | production resolver → API-key Responses,reasoning High,最小文本 turn | ✅ | sdkType=openai、useResponsesApi=true、auth=api_key,返回 DEEPSEEK_RESPONSES_OK;key 未打印 |
| 2026-08-02 | codepilot_runtime | DeepSeek official | deepseek-v4-flash | 已配置 API key | production compat adapter,thinking enabled + output_config.effort=high,最小文本 turn |
✅ | sdkType=claude-code-compat、effortSent=high、thinkingSent=true,返回 DEEPSEEK_ANTHROPIC_OK;key 未打印 |
| 2026-07-17 | codex app-server probe | isolated/no login | gpt-5.6-sol/terra/luna | 无用户凭据 | initialize + model/list | ✅ 目录与新 schema 已确认;不代表账号 entitlement | codex-cli 0.144.2,本调研文档 POC |
| 2026-07-19 | codex_runtime | Codex Account | gpt-5.6-sol | 当前登录 | 选择模型后检查 composer effort selector | ❌ selector 未出现,Phase 0 重新打开 | /api/providers/models 的 Codex 行只有 nested capabilities.supportedEffortLevels;UI 只读 top-level 字段。用户截图 + route JSON 均复现 |
| 2026-07-19 | claude_code | Kimi for Coding | kimi-for-coding | 当前配置 | 选择模型后检查 composer effort selector | ❌ selector 未出现,Phase 1 重新打开 | 启用 DB 行为 manual_enabled/user_edited,capabilities_json={};旧 catalog sonnet 行为 manual_hidden,catalog round-trip 测试未覆盖此路径 |
| 2026-07-19 | Settings | 智谱 CodePlan | — | 当前配置 | 添加模型 → 获取模型列表 | ❌ 约 8s 后 502 PROBE_FAILED |
search route 请求 /api/anthropic/v1/models;同机 scutil --dns 为 No DNS configuration available,当前网络 smoke 无效;catalog/manual 路径仍须可用 |
| 2026-07-19 | claude_code UI | Claude | Fable / Sonnet | 当前配置 | 比较模型与 effort 两个选择器 | ❌ 视觉合同不一致 | trigger 外壳同为 32px/14px/8×10,但 model label 为 mono、effort 为 sans;popover 圆角 24px vs 16px、宽 320px vs 144px,动画合同也不同 |
| 待跑 | codex_runtime | Codex Account | gpt-5.6-sol | real login | select effort → one turn | 📋 | |
| 待跑 | claude_code | GLM Coding Plan | glm-5.2 | API key | high/max 两档实际 wire + 计费差异 | 📋 移交用户统一验证 | Phase 1 已落静态声明(GLM_CODING_PLAN_MODELS),未经真实 key |
| 待跑 | claude_code | GLM Coding Plan | glm-5.2 | API key | sonnet 槽是否另有 5.2 世代 turbo SKU;[1m] 长上下文变体是否存在 |
📋 移交用户统一验证 | Phase 1 把 sonnet/opus 双槽都映到 glm-5.2、未加 [1m] 变体,均待此项定案 |
| 待跑 | claude_code | Kimi Code | kimi-for-coding | API key | 固定展示名;Auto 省略与 Low/High/Max 三档逐一发送 | 📋 本轮继续验证 | 目录已落 upstreamModelId='kimi-for-coding' + Auto/Low/High/Max;静态与 route 回归通过,真实网关接受/降级仍以此 smoke 为准 |
| 待跑 | claude_code | Kimi Code | kimi-for-coding | API key | queryOptions.effort vs CLAUDE_CODE_EFFORT_LEVEL 优先级;Kimi 渠道是否接受 effort |
📋 移交用户统一验证 | p1-effort-chain:本轮未改 env 注入代码,见决策日志 |
| 待跑 | claude_code + native | Anthropic | claude-sonnet-5 | API key/login | adaptive + effort | 📋 | |
| 2026-07-28 | claude_code CLI | Anthropic | claude-opus-5 | 当前登录 | --model claude-opus-5 --effort high --safe-mode --no-session-persistence --max-budget-usd 0.05 |
✅ route/entitlement; |
CLI 2.1.220 的结构化结果真实上报 modelUsage.claude-opus-5、canonicalModel=claude-opus-5、contextWindow=1000000;首轮总成本约 0.062 USD,触发人为 0.05 USD error_max_budget_usd,故本行不冒充完整 turn 成功 |
| 待跑 | packaged claude_code UI | Anthropic | opus-5 | 当前登录 | picker 选择 Opus 5 → 一次完整 turn → managed Sub-agent 同 route | 📋 | CLI route/entitlement 已证实;仍需验证打包客户端的目录展示、wire 与 Sub-agent 入口 |
| 待跑 | codepilot_runtime | third-party Anthropic proxy | opus-5 | API key | thinking off + Auto:不得提示“你选择了 High”;显式 xhigh 则必须按原始选择提示未发送 | 📋 | 结构化 provenance 与中英文通知已有 unit 覆盖;真实代理是否接受 thinking/默认 effort 仍须凭据 smoke,不以测试冒充 |
bfc8f1e84e5de9bdcc31c6723b18b39c |
electron-dev claude_code | Claude Code | claude-opus-5 | 当前登录 | 选择 Opus 5 → 完整回复 → 回复后模型标签保持 Opus 5 | ✅ route/turn; |
真实 turn 成功,DB 仍为 claude-opus-5;SDK 随后缓存仅 default/opus[1m]/fable/sonnet/haiku,旧实现替换目录后 UI 自动纠正成 Default。修复后 live /api/providers/models?runtime=claude_code 同时返回 canonical opus-5→claude-opus-5 与 SDK convenience entries |
| 待跑 | codepilot_runtime | OpenAI-compatible | supported model | API key | effort 透传/降级 | 📋 | |
| 2026-07-20 | codepilot_runtime | ClinePass | cline-pass/kimi-k3 | API key | direct non-stream, max_tokens=4 | ✅ HTTP 200 | 响应 model=moonshotai/kimi-k3;仅验证精确模型 ID,不代表 streaming/tool/effort 已 smoke |
| 2026-07-20 | codepilot_runtime | OpenCode Go (OpenAI) | kimi-k3 | API key | direct non-stream, max_tokens=4 | ✅ HTTP 200 | 响应 model=kimi-k3;仅验证精确模型 ID,不代表 streaming/tool/effort 已 smoke |
| 2026-07-20 | codex_runtime UI | Codex Account | gpt-5.6-sol | 当前登录 | 正常窗口选择模型并展开 effort | ✅ UI passed | 最终 models API 与输入框均显示 Default/Low/Medium/High/XHigh/Max;本行不替代一次真实 turn wire |
| 2026-07-20 | codex_runtime UI | Kimi for Coding | kimi-for-coding | 当前配置 | 真实存量 catalog 行读取并展开 effort | ✅ UI passed | 最终 route 已把 stale max-only catalog cache 合入当前 Low/High/Max;输入框显示 Default/Low/High/Max,DB 未写回;user-edited 反例保持用户能力 |
| 2026-07-20 | Settings UI | 智谱 CodePlan / GLM (CN) | — | 当前配置 | 模型 → GLM (CN) → 添加模型,上游列表不可依赖 | ✅ UI passed | 对话框约 2s 后显示内置 GLM-5.2 / GLM-4.5-Air,共 2 个上游模型,不再报列表获取失败 |
| 2026-07-20 | composer UI | Kimi / Claude-capable models | — | 当前配置 | 正常窗口 + 360×720 窄窗口打开模型与 effort 菜单 | ✅ UI passed + evidence archived | Radix collision placement 将两种 320px 菜单约束在窄窗口安全区;触发器、列表文字、圆角和行间距共享合同。证据:GPT-5.6 正常窗口 / 模型菜单 360px / Kimi effort 360px |
- 2026-08-02(DeepSeek V4 Flash 0731):Signal:DeepSeek 2026-07-31 更新 Flash、单独支持 Codex/Responses,并公布推理强度;用户要求判断是否需要适配以及 ClinePass/OpenCode Go 是否跟进。Triage:模型名没变,所以不需要新 SKU;但仓库的 DeepSeek 模型无 capability、第三方 Anthropic effort 被统一丢弃、Codex Runtime 只会把 Responses 转译回 Anthropic,没有利用官方 Flash 原生 Responses。ClinePass/OpenCode Go 只能证明模型可选,不能证明网关接受第一方 effort 字段。Fix:preset 增加模型能力与独立
wireCapabilities;resolver 以 identity+exact model 选择 transport;Flash/Codex 使用 API-key Responses,AI SDK 未识别 DeepSeek reasoning 时由 verified transport 设置forceReasoning并移除未支持 summary;CodePilot compat 把 verified effort 写入output_config.effort;旧 provider 以 preset defaults < stored override 合并;聚合渠道不动。Auto 继续表示不显式指定,未硬写官方示例的全局 max。Verify:定向 70/70 后全量npm run testexit 0;真实 DeepSeek Responses High 与 Anthropic thinking+High 两条均返回预期 marker。Guardrail:ProviderManagement/Runtime 增加 model-vs-wire 分轴、exact-model dispatch、SDK unknown-model reasoning 与 unsupported-field 规则;研究基线见docs/research/deepseek-v4-flash-0731-adaptation-2026-08-02.md。packaged UI 与 Claude Code subprocess 尚未冒充完成。 - 2026-07-28(Claude 复审 follow-up:effort provenance + 精确档位门):Signal:Opus 5
thinking off + Auto的系统兼容 High 被第三方 proxy 当成“用户选择的 High”发出假提示;Sonnet 4.6 allowlist 注释写明无xhigh,但生产门只判断模型支持 effort,外部 Codex config 仍可把xhigh发到 wire。Triage/Fix:ClaudeModelOptionsOutput增加effortProvenance,proxy drop 只认source=explicit并保留原始 requested tier;官方 effort 表增加可执行levels,wire 同时核验 model + tier,分别产出 unsupported-model / unsupported-tier / third-party-proxy 三种结构化事实,Native 两路径和 en/zh 共用 reason 映射;Codex proxy 复用同一 wire builder,因此不再外发 Sonnet 4.6xhigh。Verify:核心定向 112/112;沙箱外npm run test4736/4736;npx tsc --noEmit、生产构建通过;touched ESLint 0 error(agent-loop.ts4 条既有 unused warning)。Guardrail:allowlist 测试要求每个 pattern 同时具备 breadcrumb + levels,反例锁定 Auto 不冒充 High、调整后的 proxy 提示仍引用原始 xhigh、Sonnet 4.6 max 可发而 xhigh 不可发。真实第三方代理 packaged smoke 保持待跑,不标 Smoke passed。 - 2026-07-28(Opus 5 回复后显示 Default):Signal:用户在 electron-dev 选择 Opus 5 并完成真实回复后,composer 标签变成
Default (recommended)。Triage:会话bfc8…的 DBmodel=claude-opus-5、SDK init/turn 均成功;回复后 capability cache 捕获 5 个 convenience entries,/api/providers/models用它整表替换 canonical env 目录,删除opus-5,随后MessageInput对“缺失模型”执行合法但前提错误的 auto-correct。Fix:新增mergeEnvCatalogWithSdkModels(),保持 canonical 行及顺序,SDK 仅追加身份未重叠的新入口;重叠的移动 alias 不覆盖固定版本标签/upstream。Verify:真实 Dev 接口热更新后同时返回opus-5→claude-opus-5、default与 SDK variants;定向目录/round-trip/Opus 5 测试 44/44;npm run test(含tsc --noEmit)4737/4737;touched ESLint 与git diff --check通过。Guardrail:route 行为测试注入真实 2.1.220 五行缓存,断言opus-5不消失、default仍追加、sonnet不重复或被改名。packaged UI 仍待跑。 - 2026-07-28(Opus 5 接入 + Claude review 闭环):官方模型 ID 为
claude-opus-5,Claude Code2.1.219起加入该模型;CodePilot 新增显式opus-5,不改既有opus→4.7pin,不给未验证的第三方云目录伪造 slug。Opus 5 复用 adaptive/sampling 合同并加入 effort API allowlist;disabled thinking × xhigh/max 受控降到 high 并通过结构化、本地化 status 告知,Auto 则显式 high,消除对 CLIdefault_effort的隐含依赖。Claude review 另发现 Codex proxy 仍把所有 Anthropic effort 映射为 manualbudgetTokens;现改为使用 resolved upstream model + 共享 sanitizer/wire builder,五个 adaptive 模型均生成 adaptive + effort,第三方 proxy 至少不再收到明确非法的 manual thinking。buildEffortAdjustmentNotice的缺省模型名也从会误指 Opus 5 的硬编码改为unknown。生产链使用系统 Claude binary,本机2.1.220的真实请求已经上报claude-opus-5与 1M context;请求仅因本地 0.05 USD 保护上限被截断,按 route/entitlement 通过、完整输出待 packaged smoke 记账。门禁:最终核心定向 91/91,真实 AI SDK Anthropic 请求形状含 adaptive summarized + effort;npm run test4726/4726、npm run build、touched ESLint(0 error)与tsc --noEmit均通过。 - 2026-07-20(Kimi 档位纠偏):接受 Claude P2-1 的 stale 判断。Kimi 最新 K3 文档已从 max-only 更新为 low/high/max,且用户确认 Kimi for Coding 当前返回 K3;catalog、i18n、DB enrichment 与测试同步扩为三档。展示名和 wire ID 不变,真实
/coding/逐档 smoke 仍作为独立验收,不反推 ClinePass/OpenCode Go 网关能力。 - 2026-07-20(聚合套餐 K3):ClinePass / OpenCode Go 各自新增显式 K3 SKU,但
Kimi for Coding继续保持 latest 渠道抽象,不展示底层版本。Kimi 官方 low/high/max 只证明模型本体能力,不能自动证明两个 OpenAI-compatible 网关接受同一 effort 字段;在网关 wire smoke 前 fail closed,不声明supportsEffort。 - 2026-07-19(用户 UI smoke 打回):Phase 0/1/2 重新打开。既有 builder/catalog/unit 绿只证明局部对象成立,没有覆盖最终 route serialization、用户编辑后的手动 DB 行和真实 popover 样式;后续不得再以局部测试关闭这三类问题。
- 2026-07-19(Kimi 事实修正):官方当前把
k3与kimi-for-coding列为不同模型 ID,旧的“kimi-for-coding是稳定 latest alias、底层就是 K3”假设撤回。产品展示仍按用户要求只写Kimi for Coding;wire ID 与 capability 单独裁决,不把显示策略冒充协议事实。 - 2026-07-19(恢复修复裁决,已被 2026-07-20 档位纠偏更新):wire 继续发产品渠道自身的
kimi-for-coding,UI 只显示Kimi for Coding;当日先保守暴露 Auto/Max,随后因官方文档更新与用户渠道事实扩为 Low/High/Max。 - 2026-07-19(网络边界):本机无 DNS 会让 GLM/Kimi/Google Fonts 等第三方请求进入超时路径;本轮 502 证明当前失败处理体验,但不能证明供应商端点永久不可用。恢复 DNS 后必须重跑真实 wire smoke。
- 2026-07-17:调研确认 UI 组件已存在,计划定位为 capability / catalog / wire contract 收敛,不重做输入框。
- 2026-07-17:本地隔离 POC 确认 Codex 0.144.2 已列出 GPT-5.6,同时发现
supportedReasoningEfforts[].reasoningEffortschema drift;列为 Phase 0。 - 2026-07-17(历史基线,2026-07-20 已更新):Kimi for Coding 当时只承诺 Auto/Max;当前以最新文档为 Low/High/Max。GLM 仍只表达实际 high/max,拒绝统一菜单造成伪精度。
- 2026-07-17(用户最终取舍):
Kimi for Coding作为用户可见的最新模型抽象,固定请求kimi-for-coding;不展示或跟踪 K3 等底层版本,不新增显式k3内置入口,底层升级不触发目录改动。 - 2026-07-17(Phase 0 实施完成,commit
321654c):schema drift 与伪档位来源一并收编。- dual schema:
models.ts兼容{ effort }/{ reasoningEffort },两者同现时新字段优先;空/非字符串/未知 token fail-closed 丢弃并去重;defaultReasoningEffort不在解析结果内即清空。 - per-model allowlist:
resolveCodexEffort(effort, declaredLevels)取代 turn/start 上的全局 clamp。模型声明 xhigh/max 即原样透传(推翻"Codex 只认 minimal|low|medium|high"的旧全局前提——该前提在 GPT-5.6 之后自身成了假数据:用户选 Max、线上发 high);未声明档位 omit 而非折算到相邻档位。clampCodexEffort保留但降级为无 capability 信息(冷缓存/未登录/旧二进制)时的兜底,理由是该路径无法确认二进制是否属于会对未知 variant 致命报错的旧版本。allowlist 经getCachedCodexEffortLevels只读 warm cache 获取,维持 P0.3「turn/start 绝不 spawn」约束。 - 伪档位来源:删除
EffortSelectorDropdown.tsx:36五档硬编码回退(P2-4 裁决)。判定"无来源时不存在诚实默认值",故缺失/空/含 undefined 一律隐藏选择器;规则抽为纯函数src/lib/effort-levels.ts:resolveEffortMenuLevels以便直接单测(而非复刻组件逻辑),另加源码钉防回退复活。同理buildCodexProviderModelGroup无可识别档位时 OMITsupportedEffortLevels而非给[]。 - ultra:诚实解析入
CodexModel,但由toGenericEffortLevels挡在通用 selector 外(Codex 专属产品档位,非 Responses API effort,多代理语义未接完不承诺)。 - openai-oauth:本轮只澄清边界(静态手维护目录 / effort 服务端固定 medium / 禁止手加 5.6),不删除;是否合并或下线转 Codex 裁决,见 artifact judgment。
- 验证:
npx tsc --noEmit通过;单测 3858 passed / 1 fail。唯一 fail 为provider-request-shape.test.ts(本 worktree 缺node_modules/ai),已用git stash在 clean tree 上复现同样失败,确认与本改动无关、属环境问题。Phase 0 新增/更新用例 73 个全绿(dual-schema 旧/新/混合/全空、xhigh/max 透传、undeclared omit、ultra 隔离、cache invalidation / logged-out / 空 model/list fail-closed)。 - 未做:真实凭据 smoke(Codex Account × gpt-5.6-sol)仍待 Phase 4,Smoke Ledger 对应行保持 📋。
- dual schema:
- 2026-07-17(Phase 0 fix 轮,commit
c157809):收口 Codex 审查的 2 条 P1。- cache invalidation 生产接线(P1-1):
DELETE /api/codex/account此前只调logoutCodex(),而listCodexModels({cacheOnly:true})按 P0.3 故意忽略 TTL,于是登出/换号后 full catalog 与 turn/start allowlist 仍会用旧账号 capability 作答,违反 logged-out fail-closed。修法是新增src/lib/codex/account-transition.ts,把 logout / login start 各包一层「成功后invalidateCodexModelsCache()」,两条 route 只经该包装调用——把不变量放在包装里而不是各 route 内联,是为了让第三个调用点将来不可能忘记清 cache。login 侧在 start(而非 completed)时清:代价是一次多余重取,收益是新账号绝不可能读到旧账号目录。 - 测试形态(取舍已记录):
codex-account-cache-invalidation.test.ts只注入最底层的 JSON-RPC 调用(DI seam),cache、invalidation、buildCodexProviderModelGroup、getCachedCodexEffortLevels全部是真实实现,覆盖 warm cache → logout / 换号 → 两个读取端均 fail-closed,外加「logout 失败不清 cache」反例。route 本身用await import(),在 ESM runner 下无法被 module stub 拦截(已实测require.cache注入对动态 import 无效),故 route → wrapper 这一跳用源码钉断言(禁止裸logoutCodex/startCodexLogin复活),与既有codex-models-decoupling.test.ts的 route spawn-policy 钉同构。 - 全量门禁(P1-2):
node_modules/ai缺失致provider-request-shape.test.tsENOENT。按现有 lockfile 前台npm install(未改任何依赖版本、未改产品逻辑、lockfile 无 diff)后,npx tsc --noEmit通过,npm run test:unit全量 3894 tests / 3894 pass / 0 fail,exit 0(上一轮 3842/3841/1)。计数从 3842 升到 3894 = 恢复的provider-request-shape用例 + 本轮新增 6 个。 - 承接 Codex 三项工程裁决:①
openai-oauth本轮保留,不在缺登录态与旧会话迁移证据时下线;Phase 3 增加迁移调查,证实可平滑迁移后再收敛。② cold-cacheclampCodexEffort本轮保留为旧 binary 保守兜底;Phase 3 收敛为无 capability 时 omit 或显式降级提示,避免长期静默改写用户持久化的选择。③supportsEffort > 1当时随 Kimi 单档能力检查,当前 Kimi 已有 Low/High/Max,选择器直接由 allowlist 驱动。三项均属工程取舍,无需 human gate。
- cache invalidation 生产接线(P1-1):
- 2026-07-17(Phase 0 fix 轮 #2,commit
343891a):收口 Codex 对上一轮 F1 的复审——推翻 130 行的「route 那一跳只能用源码钉」结论。- 根因:route 无法被测试驱动,不是 ESM runner 的限制,而是 route 自己用
await import()造成的。改为静态 import + 把 DELETE / POST 主体抽成handleAccountDelete(perform = logoutCodex)/handleLoginPost(request, perform = startCodexLogin),route 导出零参数委派,生产接线仍绑定真实 RPC。DI seam 收窄到最底层 JSON-RPC 一跳。 - 测试形态:
codex-account-cache-invalidation.test.ts现在实际调用两个 route handler,覆盖 warm cache → DELETE logout、以及 login 的 chatgpt(默认空 body / 显式 kind)、chatgptDeviceCode、apiKey 四个成功分支,断言 HTTP 200、login 结果原样透传、buildCodexProviderModelGroup({cacheOnly:true}) === null、getCachedCodexEffortLevels('gpt-5.5') === undefined。反例三条:logout 失败(500)、login start 失败(500)、apiKey 缺 key(400,被拦在 transition 之前)——均不得清缓存。源码正则钉已被行为测试取代并删除。取舍理由:route 层窄 DI seam 比 fake Codex binary fixture 更小更稳,且真实执行了请求解析/分支/await/响应。 - 验证:targeted 8/8 通过;
npx tsc --noEmit通过;全量npx tsx --test --import ./src/__tests__/db-isolation.setup.ts src/__tests__/unit/*.test.ts3896 tests / 3896 pass / 0 fail、exit 0(上一轮 3894,+2 为净增用例)。 - F2 环境项:
node_modules/ai已存在,全量无 ENOENT,无需再npm install;lockfile 与依赖版本本轮零改动。
- 根因:route 无法被测试驱动,不是 ESM runner 的限制,而是 route 自己用
- 2026-07-17(Phase 1 实施,commit
de31f3b):GLM-5.2 / Kimi for Coding 目录、真实档位与映射说明落地。- GLM 目录:CN/Global 抽成共享
GLM_CODING_PLAN_MODELS(两区同一套 Coding Plan,只有 endpoint 不同)。GLM-5.2 只列一行——sonnet 与 opus 两个 role env 都指向glm-5.2,列两行就是同一模型冒充两个可选项,正是本计划要消灭的伪差异;代价是 opus 别名行从内置目录消失(存量 provider 的provider_models行不受影响,DB 行优先于 catalog)。haiku 维持glm-4.5-air(基线未称其过期)。 - GLM 两档:
supportedEffortLevels: ['high','max']。GLM 把 Claude Code 六个/efforttoken 折成两档(low/medium/high→high,xhigh/max/ultracode→max),五档菜单会让用户选low却按high计费。新增capabilities.effortNoteKey在菜单里说明这次折叠——否则两档会被读成「GLM 只有两种速度」。GLM-4.5-Air 不声明 effort → 选择器隐藏(Phase 0 的「无来源即隐藏」规则)。 - Kimi 身份:
displayName: 'Kimi for Coding',底层版本彻底不进目录(kimi-for-coding是官方稳定渠道 ID,K2.5→K3 由供应商自行滚动,跟踪版本等于每次升级都要改目录)。modelId保留sonnet(存量 DB 行与会话钉在该 id,改名会把它们弄丢),真值走新增的upstreamModelId: 'kimi-for-coding'。顺带修掉一个真 bug:此前该行没有 upstream,resolveProvider的单模型兜底(provider-resolver.ts:~590)会把裸字符串sonnet发给 Kimi。 - Kimi effort(2026-07-20 更新):
supportedEffortLevels: ['low','high','max']→ 菜单恰为 Auto + Low + High + Max。toWireEffort()继续收口auto→ 不下发,具体档位原样进入queryOptions.effort;两处发送点有源码钉防effort=auto上线。 - Moonshot 不改名(scope guard):零改动 + 新增反向断言。Moonshot 是独立 PAYG provider,按名售卖 K2.5 SKU;
Kimi for Coding是 Kimi Coding Plan 的渠道抽象,套上去等于虚构它没有的渠道。Volcengine / 百炼 的kimi-k2.5条目同理不动。legacy-catalog-hint.test.ts未改动即全绿——它 pin 的是「用户手加kimi-k2.5不该报 legacy badge」,与内置目录改名正交。 - 验证:
npx tsc --noEmit通过;全量单测 3922 tests / 3922 pass / 0 fail、exit 0(基线 3896,+26 为本轮新增)。新增覆盖:GLM 目录世代 / 两档语义 / 伪档位不可达 / 映射说明 en+zh、Kimi 渠道 ID / 版本名不外泄 / 无 k3 条目 / Auto 不外发 / Max 外发 max、Moonshot 不改名、toWireEffort与两处发送点源码钉。 - 待真实凭据验证(Smoke Ledger 已留行,移交用户统一验证):① GLM sonnet 槽是否另有 5.2 世代 turbo SKU;② GLM
[1m]长上下文变体(无凭据前加变体等于凭空声明能力,故未加);③ Kimi Auto 是否实际落到 Max;④queryOptions.effort与CLAUDE_CODE_EFFORT_LEVEL优先级及 Kimi 兼容性。 - p1-effort-chain 结论:本轮不改 env 注入代码,也不预设 env-only。现状读码事实:composer 选择 →
toWireEffort→ API bodyeffort→ 既有queryOptions.effort链路;CLAUDE_CODE_EFFORT_LEVEL仅出现在provider-resolver.ts的MANAGED_ENV_KEYS清理清单里,当前无任何代码写入它。因此"优先级"目前是理论问题,须以真实凭据 smoke 定案后再决定是否需要 env 注入——在此之前新增 env 写入就是未经验证的猜测。 - 转 Codex 裁决的工程取舍:① GLM
defaultEnvOverrides的 role mapping 今天是死代码——toClaudeCodeEnv的HOST_MANAGED_ANTHROPIC_ENV_KEYS(provider-resolver.ts:448)显式跳过 extra_env 里的ANTHROPIC_DEFAULT_*_MODEL,而 GLM 没有defaultRoleModels,故ANTHROPIC_DEFAULT_SONNET_MODEL从未进入子进程 env(唯一写入点是provider-resolver.ts:423,取自roleModels);GLM 的 alias 实际由其网关服务端解析。本轮按 required check 把值更新到 5.2 世代并加注释,但没有改用defaultRoleModels使其生效——那会改变 wire 行为,且roleModelForEnv会对 GLM 的自指 alias 调用canonicalAnthropicAliasUpstream,把sonnet规范成 Claude 的模型 id,无凭据验证下风险明确大于收益。建议列 Phase 3 收敛(要么删掉这些误导性的 seed 值,要么改走 role mapping 并配 smoke)。② 存量 GLM/Kimi provider 的 picker 仍显示旧名:DBprovider_models行优先于 catalog(route.ts:238-247),故本轮改名只对新添加的 provider 与手动 refresh 生效;是否需要一次 catalog re-seed 迁移,请裁决。③supportsEffort > 1在 Codex 动态目录专属;Kimi 走 catalog 静态声明,当前 Low/High/Max allowlist 直接驱动 Auto + 三档菜单,不依赖该阈值。
- GLM 目录:CN/Global 抽成共享
- 2026-07-17(Phase 1 fix 轮 #1,commit
292bcce):修 Codex 审查两条 finding——catalog capabilities 跨 DB round-trip 保真 + GLM 映射文案补ultracode。- P1 根因(capabilities 在 DB round-trip 中丢失):
db.ts的seedCatalogModelsIfEmpty()与alignEnabledWithCatalog()两条 sync 路径都把capabilities_json硬写成'{}',而读侧(api/providers/models/route.ts:238-246、provider-resolver.ts:1013-1031)让同 ID 的 DB 行遮蔽 catalog。后果:GLM/Kimi 行一旦被 Models 页首次 GET materialize,supportsEffort/supportedEffortLevels/effortNoteKey全丢,Phase 1 刚加的 Auto/High/Max 菜单在真实使用路径上根本不出现;存量 Kimi 行还会继续显示Kimi K2.5并发裸sonnet。这直接违反语义验收「用户可见字段必须有真实 source breadcrumb」——上一轮的断言只跑在 catalog 对象上,看不见这条路径。 - 改法:抽出
CatalogSyncModel结构类型 +serializeCatalogCapabilities(),insert/enable 两路都写capabilities_json,fieldsAlreadyMatch一并比较。安全边界:只同步user_edited=0且非manual_*的系统管理行,且只碰display_name/upstream_model_id/capabilities_json——model_id不动,session 的 provider/model pin 不受影响。catalog 未声明 capabilities 时用COALESCE(?, capabilities_json)传 null 保持原值,避免把 API discovery 得到的能力(如 GLM haiku)擦成'{}'。 - 顺带答复上一轮转裁决的第 ②项(存量 provider picker 仍显示旧名 / 是否需要 re-seed 迁移):Codex 已裁决「合同必须覆盖存量系统管理行」,本轮即按此实现——存量行经
alignEnabledWithCatalogrealign 到Kimi for Coding+kimi-for-coding,无需另做一次性迁移脚本。 - P2(GLM 映射文案):en/zh 的说明只写
xhigh/max→Max,漏掉基线明确列出的ultracode,与代码注释所称「六档折两档」不一致。两语言补全,并把测试从「key 存在」加强为断言两组完整映射语义。 - 验证:
npx tsc --noEmit通过(无输出);npm run test(typecheck + 全量单测)3929 tests / 3929 pass / 0 fail(上一轮基线 3922,+7 为本轮新增)。负向对照:git stash掉db.ts修复后重跑,GLM 与 Kimi 两个 round-trip suite 确实 fail(3926 pass / 3 fail),证明新测试能抓到该 bug 而非空转;恢复后复绿。private-marker 双面 grep 0 命中。
- P1 根因(capabilities 在 DB round-trip 中丢失):
- 2026-07-18(Phase 2 实施 + 承接权限计划 s00 计数勘误,commit
eb75df8):Claude Sonnet 5 完整接入 + agent-loop effort 裁决落地。- s05 effort 裁决 = 恢复下发(非降级隐藏):读实装
@ai-sdk/anthropic@4.0.5的dist/index.js,effort 经 GAoutput_config.effort请求字段下发,全文件无effort-2025-11-24/ 任何 effort beta header(grep 0 命中)。旧 workaround(agent-loop.ts:408对 adaptive 家族丢 effort + 发RUNTIME_EFFORT_IGNORED)的前提「装的包仍挂过期 beta」已失效,官方亦确认 Sonnet 5 / Fable 5 / Opus 4.7+ GA 支持 effort。故推翻丢弃逻辑:native 官方路径对所有模型透传 effort,去掉该路径的 ignored toast(第三方代理路径保留——代理确实可能不认字段);agent-loop-toolloop-poc.ts同步保持 parity。UI 选择 = wire = catalog 声明四者一致,闭合本计划「四者一致」验收。工程取舍(恢复 vs 隐藏)按用户规则不挂 human gate,理由写在此处交 Codex 复审。 - Sonnet 5 与 Fable 5 的关键差异(不得错用 forced-on):新增
SONNET_5_PATTERN并入isOpusAdaptiveThinkingModel(共享 manual→adaptive、1M 默认无 beta、effort 透传),但不并入 fable 的thinkingForcedOn分支——官方 Sonnet 5 允许thinking:{type:'disabled'}(Fable 5 会 400),故 disabled 原样透传(Opus 4.8 语义)。sonnet-5-model.test.ts专测该差异 +sonnet-5不误伤非 adaptive 的sonnet-4-6。 - catalog / 派生链 / context:
ANTHROPIC_FIRST_PARTY_MODELS加sonnet-5→claude-sonnet-5(无 role,不劫持sonnet默认,sonnet-4-6pin 不迁移,resolve-session-model.test.ts断言);env/route/resolver/client fallback 经ENV_CLAUDE_CODE_MODELS自动含之;model-context.ts加 1M + 新 tokenizer +30% 注记(budget 估算防低估)。OpenRouter/Bedrock/Vertex slug 未验证不加(fable 纪律,测试 pin 住)。 - sampling 约束:按 fable 先例——app 从不为 Anthropic 请求组装 temperature/top_p/top_k(grep
agent-loop.ts/claude-client.ts0 命中),故 sanitizer「无可剥离」,只补注释说明约束 + 未来若加 sampling 旋钮必须过此函数;不加会成死代码的防御分支(硬规则禁「为过测试加防御复杂度」)。 - s07 模型切换回 Auto:新纯函数
resolveEffortAfterModelSwitch(currentEffort, newLevels),ChatView.handleProviderModelChange接线并限 manual pick(isAuto 自动纠正路径 early-return 之后才跑——避免在用户没主动切换的静默纠正上弹 toast,本身也是「manual-only side effect」原则;codex-phase-6-wiring.test.ts的 isAuto early-return 钉据此保持不破)。新 i18nmessageInput.effort.resetOnModelSwitch(en/zh)。 - 验证:
npx tsc --noEmit通过(无输出);canonical 全量(HOME干净 +CODEX_DISABLED=1,经 Node child-process wrapper 绕开 shell env 前缀审批门)4209 tests / 4209 pass / 0 fail / 1039 suites、exit 0(基线 4186,+23 为本轮新增:sonnet-5 家族 / disabled 差异 / 1M / catalog pin / env-route / 切档 helper / no-migration)。self-inflicted 修 1 处(注释含RUNTIME_EFFORT_IGNORED字面量触发 doesNotMatch,改措辞)。private-marker 双面 grep 0 命中。
- s05 effort 裁决 = 恢复下发(非降级隐藏):读实装
- 2026-07-18(Phase 2 复审轮 #1 修复,commit
bd38d49):Codex 复审 4 条(2×P1 + 2×P2),受控修复轮,只改这 4 条 + 三处进度回写。- P1 / s04 sampling guard(从注释升级为主动 guard):Codex 指
ClaudeModelOptionsInput无 sampling 入口、sanitizer 不拒绝/剥离非默认 sampling、无告知信号,仅靠「现有调用方不组装」不能关闭该合同。改法:ClaudeModelOptionsInput新增temperature/topP/topK;sanitizer 对 adaptive 家族剥离非默认值(temperature≠1、任何显式 top_p/top_k)到strippedSamplingParams告知数组、safe 值留sampling(同thinkingForcedOn的 surface-don't-swallow 规则),非 adaptive(sonnet-4-6)原样透传不误伤。sonnet-5-model.test.ts新增 7 分支:temperature 非默认剥离/=1 放行、top_p/top_k 任意值剥离、全剥离、无 sampling 空、4.6 不误伤、fable/opus 家族一致。未加 live toast:当前无调用方组装 sampling,接一条永不触发的 toast 路径 = 死代码(硬规则禁为过测试加防御复杂度);告知信号以 sanitizer 返回值形态存在,未来调用方接入即得。 - P1 / s05 Native effort wire 行为测试(源码 pin→可执行对象断言):Codex 指原测试只断言 sanitizer 返回 effort + 源码文本含
anthropicOpts.effort,未捕获真实 providerOptions。改法:wire 构造抽到agent-loop-anthropic-wire.ts(buildAnthropicProviderOptions,随agent-loop-error-event.ts先例,无 DB/provider 依赖可轻量单测),agent-loop.ts消费其返回并把.anthropic直接赋给providerOptions(同一对象);agent-loop-anthropic-wire.test.ts断言 sonnet-5+xhigh 官方路径providerOptions.anthropic.effort==='xhigh'且effortDroppedForProxy===false、代理路径 effort 不上线路且 drop 信号=true、adaptive 家族(fable/opus4.8/4.7)官方均透传。fable-5-model.test.ts的旧 agent-loop 源码 pin 重指向新模块 + 断言 agent-loop 经 helper 接线。 - P2 / s07 ChatView 切换行为测试(grep→可执行 4 观察点):Codex 指原 wiring 测试只 grep helper 名与 i18n key,未断言
setSelectedEffort(undefined)/ 兼容档不变 / toast 一次 / isAuto 无副作用。改法:新增resolveModelSwitchEffortEffect(isAuto, currentEffort, levels)→{resetEffort, showResetToast}把 isAuto 门 + reset/toast 决策收成可执行函数,ChatView 改为消费它(isAuto 分支现为 live 代码——effort-effect 块移到 isAuto persist-skip 之前,行为不变:isAuto 仍在 session PATCH 前 early-return)。effort-menu-levels.test.ts加行为测试断言 4 观察点:manual+不支持→reset+toast、manual+兼容→零副作用、reset⇔toast 锁步(单布尔=结构性一次)、isAuto→零副作用。codex-phase-6-wiring.test.ts的 isAuto 钉从固定距离 grep 升级为「isAuto return 在 session PATCH 之前」的顺序不变量断言。 - P2 / s00 账本因果表述(撤回单因子归因):Codex 受控对照证明「保持同一 clean HOME、仅去掉
CODEX_DISABLED=1仍为 4186/4186/1032」,原勘误把 4186 vs 4170 差值单独归因给CODEX_DISABLED不成立。改法:runtime-permission-modes.md复审轮 #6 勘误改为只陈述已实测事实(4186 canonical 计数真实、shared-clone 复现),撤回单因子归因,补本 run 在当前 HEAD 的单变量对照(CODEX_DISABLED=1与去掉它均 4227/4227/1043,scripts/tmp-*-runner.mjs),把原差值归到「当时裸命令用了不同 HOME/环境」,不再下因果断言。 - 验证:
npx tsc --noEmit通过(无输出);canonical 全量(HOME干净 +CODEX_DISABLED=1,Node child-process wrapper)4227 tests / 4227 pass / 0 fail / 1043 suites、exit 0(基线 4209,+18 为本轮新增:s04 sampling 7 + s05 wire 6 + s07 effect 5,减去 fable/phase-6 两处 pin 改写净变)。单变量对照(同 HOME、去CODEX_DISABLED)亦 4227/4227/1043,佐证 s00 更正。private-marker 双面 grep 0 命中。 - 未做:Anthropic key × sonnet-5 真实凭据 smoke(native + Claude Code 两路径的 thinking/effort/sampling 请求形状)留 Smoke Ledger 📋,移交用户。
- 新增覆盖(
catalog-capabilities-roundtrip.test.ts):fresh seed → resolver 仍见 GLM[high,max]+ note key;legacyKimi K2.5行 → realign 到渠道名/kimi-for-coding/['max']且model_id不动;user_edited=1与manual_hidden反例 → 展示名、upstream、capabilities 全部不被覆盖;catalog 静默 → 不擦除已发现能力。 - 待真实凭据验证项不变(Smoke Ledger 保持 📋,移交用户统一验证):① GLM sonnet 槽是否另有 5.2 世代 turbo SKU;② GLM
[1m]变体;③ Kimi Auto 是否实际落到 Max;④p1-effort-chain:queryOptions.effort与CLAUDE_CODE_EFFORT_LEVEL优先级及 Kimi 渠道兼容性——本轮仍不改 env 注入代码,结论同上一轮。
- P1 / s04 sampling guard(从注释升级为主动 guard):Codex 指
- 2026-07-18(Phase 2 复审轮 #2 修复,commit
0f9985f):Codex 复审 fix_requested 2 条(1×P1 s07 一致性 + 1×P2 账本),受控修复轮,只改这 2 条 + 三处进度回写。- P1 / s07 effort 一致性根因(原实现 manual-only,两处漏接):复审轮 #1 的
resolveModelSwitchEffortEffect(isAuto, …)刻意让isAuto分支零副作用,ChatView自动纠正到不支持旧档位的模型后保留该档位;发送处toWireEffort(selectedEffort)原样下发。更直接的是新会话入口chat/page.tsx的onProviderModelChange完全没有 effort 回退。结果:Sonnet 5 的xhigh切到 Sonnet 4.6(无 xhigh)等场景仍显示并发送xhigh,违反「UI 所选 = 发送参数 = 供应商档位」三者一致。 - 工程裁决(承接 Codex 方向):「不持久化 auto-fallback 的 session pin」与「清除当前非法瞬态 effort」是两件事。前者不变(调用点仍在
opts?.isAuto时 early-return,不写 session PATCH / localStorage);后者必须在每个有效模型变化入口执行,与 manual/auto 无关。故resolveModelSwitchEffortEffect去掉 isAuto 参数,effort 效果纯由(currentEffort, newLevels)决定;resetEffort ⇔ showResetToast仍锁步,reset 后选择即 Auto,永不再触发,故提示天然一次。 - 同一真源校验(复用 picker feed):新增
MessageInput.emitProviderModelChange包装器,从与 picker 渲染相同的providerGroups/modelOptions(useProviderModels)解析新模型supportedEffortLevels,经onProviderModelChange的opts.supportedEffortLevels下发;手动 picker(ModelSelectorDropdown)与 auto-correct effect 两路径都改经该包装器。ChatView优先消费opts.supportedEffortLevels(回退自身useProviderModels查表),chat/page.tsx直接消费opts.supportedEffortLevels并补一次性 sourced toast(messageInput.effort.resetOnModelSwitch,en/zh 已有,无新增文案)。两入口的 effort 清除都在if (opts?.isAuto) return之前执行。 - 测试(可执行 + source pin):
effort-menu-levels.test.ts重写resolveModelSwitchEffortEffectdescribe——(1) 不支持→reset+toast、(2) 兼容→零副作用、(3) reset⇔toast 锁步、(4) manual 与 auto-correct 对同一不支持输入清除一致(原「isAuto→零副作用」断言被推翻)、(5) 清除后toWireEffort(effort)===undefined(被清档位不上线路)、Auto/unset 不触发;两入口 source pin 断言 helper 调用在 isAuto persist-skip 之前;MessageInput feed 下发 pin(supportedEffortLevels: option?.supportedEffortLevels+ 两路径经包装器)。codex-phase-6-wiring.test.ts的 isAuto 钉从「onProviderModelChange?.(…{isAuto:true})」改为「emitProviderModelChange(…{isAuto:true})+ 包装器...opts转发」两跳保 isAuto。 - P2 / 账本过早关闭:s07 缺口使原「Phase 2 完成 / 模型切换回 Auto 已落地」过早。本轮按新 commit 与独立复跑真实数同步回写执行清单、状态总览、frontmatter、决策日志四处。
- 验证:
npx tsc --noEmit通过(无输出);canonical 全量(HOME干净 +CODEX_DISABLED=1,经 Node child-process wrapper 绕开 shell env 前缀审批门)4233 tests / 4233 pass / 0 fail / 1044 suites、exit 0(基线 4227,+6 为本轮净增:s07 effect describe 重写新增观察点 + 两入口/feed source pin,减去 codex-phase-6 钉改写)。npm run build编译成功(1 条既存 NFT trace warning)。private-marker 双面 grep(diff + commit message)0 命中。未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。
- P1 / s07 effort 一致性根因(原实现 manual-only,两处漏接):复审轮 #1 的
- 2026-07-18(Phase 2 复审轮 #3 修复,commit
aa2623e):Codex 复审 fix_requested 2 条(1×P1 s07 MessageInput 受控显示 + 1×P2 账本),受控修复轮,只改这 2 条 + s00 账本刷新 + 三处进度回写。- P1 / s07 根因(复审轮 #2 只修了父层,MessageInput 显示层仍漏):复审轮 #2 让
ChatView/chat/page.tsx两入口setSelectedEffort(undefined),但MessageInput.tsx:1084的selectedEffort = effortProp ?? localEffort是受控/非受控混用:用户先选xhigh时setSelectedEffort同时写localEffort='xhigh'并onEffortChange('xhigh');父层 reset 到undefined后,effortProp=undefined ?? localEffort='xhigh'重新露出 stale local 值,按钮仍显示xhigh,而父发送态已是undefined、toWireEffort省略 effort——实际组件从未回到 Auto,仍违反四者一致。复审轮 #2 的effort-menu-levels.test.ts只测纯函数(resolveModelSwitchEffortEffect)与源码顺序,从未经过这段显示解析,故没抓到。 - 改法(真正受控显示,非补丁):显示解析抽成纯函数
resolveComposerEffortDisplay(controlledEffort, localEffort, isControlled)落在effort-levels.ts(MessageInput实际调用它,非复制逻辑)。当父层拥有 effort(onEffortChange !== undefined,三个真实调用点都接)→isControlled=true→ 显示 =controlledEffort ?? 'auto',永不读 stale local;localEffort仅保留给无onEffortChange的独立用法(受控/非受控组件惯例,如<input>)。setSelectedEffort仍照旧写 local + 调onEffortChange(保留 local 是为了让回归测试能带着 stale local 断言显示仍 Auto)。 - 测试(真实全链行为,取代 source-only):本套件无 React 渲染器(无 jsdom/testing-library,同
card-primitives.test.ts约束)。新增makeComposerHarness把 composer + 父层建模成一台状态机,每个决策都是真实导出 helper:按钮标签来自resolveComposerEffortDisplay、reset 来自resolveModelSwitchEffortEffect、wire 来自toWireEffort,只有 React 状态存储(一个变量)与 toast 计数是建模胶水、非逻辑。断言矩阵:两入口(ChatView/新会话,共享同一 helper)× manual/auto-correct,先选xhigh再切 Sonnet 4.6 → 按钮显示 Auto + wire 省略 effort + toast 恰好一次;支持档位存活不 reset;reset 后二次不支持切换静默(toast 不 double-fire);resolveComposerEffortDisplay单元守卫(受控 undefined + stale localxhigh→ Auto、受控值压过 local、非受控回退 local)。补 MessageInput 源码钉:调用受控解析、const selectedEffort = effortProp ?? localEffort不复活。旧的「仅 source pin」自夸未删但降级为辅助接线守卫,行为测试为主证据。 - 验证:
npx tsc --noEmit通过(无输出);canonical 全量(HOME=/tmp/codex-i31-home干净 +CODEX_DISABLED=1,Node child-process wrapper)4241 tests / 4241 pass / 0 fail / 1045 suites、exit 0(基线 4233,+8 为本轮净增:全链行为 7 + MessageInput 受控显示源码钉 1)。单变量对照(同 HOME、去CODEX_DISABLED)亦 4241/4241/1045。private-marker 双面 grep(diff + commit message)0 命中。〔复审轮 #4 s00 更正:此 4241/1045 是暖/无丢弃时的完整计数;并行--test-force-exit命令在冷 HOME 首跑会因 force-exit 竞态欠计数且不确定(实测 4234/4203、Codex 4238),可复现的权威完整计数须用串行--test-concurrency=1=4241/1044,详见下方复审轮 #4 条目。〕 - s00 账本刷新:
runtime-permission-modes.md复审轮 #6 计数勘误早前引用的「当前 HEAD 4227/4227/1043」已随本计划推进过期;本 turn 同一干净 HOME 前台复跑 canonical,真实 TAP footer 4241/4241/1045 已回写,并注明与 d3e7041 钉数 4186 的 +55 差值全来自共享套件累积新增(非环境),单变量对照再次佐证。4186 作为 d3e7041 那棵树的钉数不改。〔复审轮 #4 s00 更正:本条「4241/4241/1045 = 干净 HOME canonical 首跑、差值全非环境」对并行原命令过强、已在复审轮 #4 撤回——冷 HOME 首跑实测 4234/4203/(Codex 4238)为 force-exit 竞态欠计数、不确定;完整计数 4241 由 HEAD 决定且与环境无关这点成立,但须用串行才能可复现地取到。见下方复审轮 #4。〕 - 未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。
- P1 / s07 根因(复审轮 #2 只修了父层,MessageInput 显示层仍漏):复审轮 #2 让
- 2026-07-18(Phase 2 复审轮 #4 修复 = s00 测试账本真实性收口,commit
65a71ab):Codex 复审 fix_requested 1 条 P2(human gate 后经用户批准的受控修复轮,只改这 1 条 + 账本回写)。争点:runtime-permission-modes.md复审轮 #6 附近与本计划把4241/1045记成「干净 HOME 单次 canonical 首跑」,但 Codex 以独立干净首跑得4238/1044,4241只在同一 HOME 二跑(暖)出现——要求复现并解释这 3 tests/1 suite 状态依赖,别把二跑 footer 冒充首跑。- 定位结论(非条件性注册、非 HOME 依赖、非环境计数语义——是 force-exit 竞态):以全新空 HOME(每个只用一次)受控复现:并行 +
--test-force-exit冷 HOME 首跑三次分别# tests 4234/# suites 1042、4203/1039(Codex 侧4238/1044),全# fail 0;同一 HOME 二跑(暖)才稳定4241/1045。用 TAP 名字 diff 冷 vs 暖,只出现在暖跑的用例含parallel-safety.test.ts的纯函数 leaf(Read + Bash does NOT parallelize、two Grep calls parallelize等——该文件无 fs / 无 HOME / 无 async / 无条件describe,不可能条件注册)与coding-plan-discovery-gate.test.ts若干。故根因不是「首跑写 HOME 状态、二跑据此条件注册」(Codex 初始怀疑方向被证伪),而是--test-force-exit与并行 worker 上报之间的竞态:冷启动慢时 worker 已完成但结果未 flush 就被强制退出丢弃 ⇒ 冷跑欠计数且不确定(故 4234/4203/4238 各不相同),暖跑因 worker 起得快、赶在 force-exit 前 flush 到齐才稳定 4241。去掉--test-force-exit则整套挂起(存在悬挂 handle,这是原命令必带 force-exit 的原因,也是不能简单去掉它的原因)。 - 权威可复现 clean-run footer(串行消竞态):全新空 HOME +
--test-concurrency=1 --test-force-exit单跑 =# tests 4241 / # suites 1044 / # pass 4241 / # fail 0 / # duration_ms 59432.085625,确定可复现(tests=4241 与暖并行一致;suites1044(串行)/1045(并行) 是 node:test 根套件在不同并发/force-exit 下 ±1 聚合抖动,tests 为准)。 - 改动:① 不改任何测试文件——注册本就确定、非 HOME 依赖,无隔离 bug 可修;② 不改 canonical 命令——超出本 P2 scope;③ 账本回写:
runtime-permission-modes.md复审轮 #6 条目与本计划 #3 条目的「干净 HOME 单次 canonical 首跑=4241、差值非环境」过强表述已撤回并注明并行冷跑欠计数竞态,权威完整计数改记串行 4241/1044 原始 footer;本计划顶部状态与 Phase 2 状态行的 4241 保留(是完整计数、成立),补「串行可复现」限定。 - 验证:
npm run typecheck(=tsc --noEmit)exit 0 无输出;串行 canonical 全新空 HOME 单跑# tests 4241 / # suites 1044 / # pass 4241 / # fail 0;并行 canonical 冷 HOME 首跑复现4234/1042+4203/1039(# fail 0),暖跑4241/1045——冷/暖差异即上文竞态。private-marker 双面 grep(diff + commit message)0 命中。未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。
- 定位结论(非条件性注册、非 HOME 依赖、非环境计数语义——是 force-exit 竞态):以全新空 HOME(每个只用一次)受控复现:并行 +
- 2026-07-18(Phase 2 复审轮 #5 修复 = s00 原样 canonical footer 收口 + s11 三处一致,机械文档轮,受控派发,仅改
runtime-permission-modes.md与本计划两份账本 md、不碰任何代码/测试/canonical 命令):Codex 复审 fix_requested 2 条 P2。审计目标快照 HEAD03ca9b0(实现正文65a71ab/aa2623e)。- P2 / s00 原样 footer(已修):上一版把加了
--test-concurrency=1的串行4241/1044footer 定为「权威替代值」、原样并行命令只留4234/4203不完整摘要,等于用串行 footer 冒充原样 canonical 命令结果,不符 s00「粘贴原样命令该次真实 footer」。回写:runtime-permission-modes.md复审轮 #6 与本计划顶部/状态/清单把 reviewer 全新空 HOME 原样并行 canonical(--test-force-exit、无--test-concurrency=1)单次首跑真实 footer 原文回写:# tests 4241 / # suites 1045 / # pass 4241 / # fail 0 / # cancelled 0 / # skipped 0 / # todo 0 / # duration_ms 8220.997917。本 run 在另一全新空 HOME 原样复跑同一并行命令,首跑得# tests 4223 / # suites 1042 / # pass 4223 / # fail 0 / # cancelled 0 / # skipped 0 / # todo 0 / # duration_ms 8038.652042(同 0 fail),与 reviewer 4241/1045 并列证明并行--test-force-exit冷 HOME 首跑计数不确定(曾观察 4234/4203/4238/4223、亦有满 4241)。据此把原『冷 HOME 首跑会欠计数』的必然表述改为『可能/曾观察到不确定欠计数』(force-exit 与并行 worker flush 竞态工程定性保留,仅去掉「每次必然」);串行--test-concurrency=1=4241/1044降为辅助确定性对照(消竞态取全量、非原样命令产物)。 - P2 / s11 三处一致(已修):Phase 2 执行清单(新增「Phase 2 目标快照」行)、状态总览表(Phase 2 行)+ frontmatter 状态行、本决策日志三处统一含目标快照 commit
03ca9b0(实现正文65a71ab)+ canonical 完整计数 4241/1045(0 fail);决策日志此前已记65a71ab+计数,本轮补齐清单与状态总览两处的 commit hash,三处对齐。 - 验证:原样并行 canonical(全新空 HOME、Node child-process wrapper 绕 shell env 前缀审批门)首跑
# tests 4223 / # suites 1042 / # pass 4223 / # fail 0、exit 0(并行冷跑竞态欠计数的又一实例、非回归——# fail 0);docs-only 改动,pre-commit 走 docs 快路(跳过 tsc + 单测);private-marker 双面 grep(diff + commit message)0 命中。未改任何代码/测试文件,未改 canonical 命令。未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。(本轮文档收口 commitca00e0e)
- P2 / s00 原样 footer(已修):上一版把加了
- 2026-07-18(Phase 2 复审轮 #6 修复 = s05 effort 能力门 + s04 sampling 告知链,commit
a7c6795;受控指导 fix 轮,用户已批准,只改这 2 条):Codex 复审 fix_requested 2 条(1×P1 + 1×P2)。- P1 / s05 根因(「不再对家族丢弃」被写成「对所有模型无条件透传」):复审轮 #1 把 wire 构造抽成
buildAnthropicProviderOptions时没有把 model 传进去,官方分支只看if (sanitized.effort)。Codex 独立行为复现claude-haiku-4-5-20251001+max→{"effort":"max"},而 Anthropic 官方 effort supported-model 列表不含 Haiku 4.5。即上一轮的裁决方向(GA effort 已可下发)成立,但落地面过宽。 - 改法(per-model allowlist,与 Run 1 在 Codex 路径的 per-model 判断模式对齐):
claude-model-options.ts新增ANTHROPIC_API_EFFORT_MODELS(每条带官方 breadcrumb)+anthropicApiSupportsEffort(),未知模型 fail-closed 为不支持(省略 effort 只是退回模型默认深度,发不支持字段则可能整轮 400)。刻意不复用 catalogcapabilities.supportedEffortLevels:那是 UI picker / Claude Code CLI(SDK runtime)的能力面,比 API effort 列表更宽——first-partyclaude-haiku-4-5-20251001在 catalog 里正声明['low','medium','high'],拿它当 wire 门等于把本 finding 原样写回;两者是不同问题("manual thinking 会不会 400" vs "API 收不收 effort"),故保持独立轴、各自 breadcrumb。wire 层收model(必填,让所有调用点被类型系统强制更新)并新增effortDroppedUnsupportedModel信号;agent-loop.ts与agent-loop-toolloop-poc.ts(后者本轮改为消费同一 helper,消除两条 native 路径的 drift)据此各发一次RUNTIME_EFFORT_IGNORED,文案如实说明「该模型不支持 effort、按自身默认推理深度运行」——与真实行为一致,不是「代理可能不支持」那句代理专用文案。 - P2 / s04 根因(告知信号只存在于返回对象):
strippedSamplingParams在生产代码零消费者,且两条 Runtime 压根没把 sampling 字段传进 sanitizer——剥离只有测试看得见,违反 sanitizer 自己写的 surface-don't-swallow 与语义验收「真实来源缺失必须隐藏或降级说明」。 - 改法(接线 + 一次明确通知,两条 Runtime 一致):
AgentLoopOptions/ClaudeStreamOptions/RuntimeStreamOptions加temperature/topP/topK并逐层透传到两条 Runtime 的 sanitizer 调用点;新增共享决策模块anthropic-sampling-notice.ts:buildSamplingIgnoredNotice(随agent-loop-anthropic-wire.ts先例,无依赖可直测),两条 Runtime 都消费它发一次SAMPLING_PARAMS_IGNORED,并把该 code 加进TOAST_STATUS_CODES(否则 toast 会被下一条 status 顶掉,等于没通知)。两条 Runtime 唯一差异是真实行为差异而非策略选择:native 把存活的sanitized.samplingspread 进streamText(故只告知被剥离的),SDK 的query()没有任何 sampling 旋钮(故剥离与存活一并如实告知)。当前无 UI 暴露 sampling,故线上行为逐字节不变——接线的意义是让 guard 从「by construction 安全」变成「真的会响」。 - 工程取舍(交 Codex 裁决):新文案沿用现有 SSE status notification 的服务端英文形态(与既有
THINKING_ALWAYS_ON/RUNTIME_EFFORT_IGNORED完全一致),未新增 i18n key——locale 只存在于I18nProvider的 React state,maybeShowStatusToast是非 hook 的普通函数拿不到它,做本地化要改 toast 路由层并顺带改掉既有两条 code 的文案,超出本轮两条 finding 的 scope。建议:把「SSE notification 统一本地化(按 code 映射 i18n key)」作为独立小项或 tech-debt 处理;若 Codex 认为必须本轮做,我再补。 - 验证:
npx tsc --noEmitexit 0 无输出;canonical 全量单测(前台单次,见下方命令说明)# tests 4235 / # suites 1042 / # pass 4235 / # fail 0 / # cancelled 0 / # skipped 0 / # todo 0 / # duration_ms 8590.0715;targeted:agent-loop-anthropic-wire.test.ts13/13(含 Haiku 4.5+max 无 effort 键、未知模型 fail-closed、Sonnet 5+xhigh 正例三类请求形状)、新anthropic-sampling-notice.test.ts13/13、fable-5-model.test.ts15/15、家族回归(sonnet-5 / opus-4-8-sonnet-4-6 / effort-menu-levels / env-models-single-source)全绿。private-marker 双面 grep(diff + commit message)0 命中。 - 命令环境差异(如实记录):本 run 的 shell 权限门禁不放行带
HOME=... CODEX_DISABLED=1env 前缀的node命令(多次尝试均被拒),故实跑为npx tsx --test-force-exit --test --import ./src/__tests__/db-isolation.setup.ts src/__tests__/unit/*.test.ts——同一 node:test 并行 + force-exit 形态,差别是 runner 入口(npx tsxvsnode --import tsx)与未设CODEX_DISABLED/ 自定义HOME。所得4235/1042落在本计划复审轮 #4/#5 已确认的并行冷跑不确定区间内(曾观察 4203/4223/4234/4238/4241),# fail 0,且本轮净增 16 例(wire +3、sampling notice +13)已包含在内。不声称它与 canonical 前缀命令逐数字等价——按 s00 既有结论,完整计数由 HEAD 决定,确定性对照须用串行--test-concurrency=1。 - 未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户;未改 canonical 命令、未动本轮两条 finding 以外的任何行为。
- P1 / s05 根因(「不再对家族丢弃」被写成「对所有模型无条件透传」):复审轮 #1 把 wire 构造抽成
- 2026-07-18(Phase 2 复审轮 #8 = s00/s11 账本按命令语义分栏,纯文档轮,受控派发,仅改
runtime-permission-modes.md+ 本计划两份 md,未碰任何代码 / 测试 / canonical 命令):- P2 / s00+s11 根因:复审轮 #7 把
4265 / 1049定性为「本次未欠计数 / 当前唯一有效完整值」。Codex reviewer 在全新空 HOME 前台原样跑 canonical 得4268 / 1050,随后仅追加--test-concurrency=1的确定性串行对照得4289 / 1055——证明并行 footer 可以是一次真实输出,却不是完整注册量,原「完整值」定性属反假数据意义上的语义失真。 - 改法(两组数按命令语义分栏,互不冒充):全部账本位置统一写入两行——
- 该次并行观测值(canonical 原样命令,计数随 force-exit 竞态逐次浮动):
# tests 4268 / # suites 1050 / # pass 4268 / # fail 0(reviewer 原样 footer,exit 0)。 - 完整注册量对照(
--test-concurrency=1,非 canonical 原样命令):# tests 4289 / # suites 1055 / # pass 4289 / # fail 0(exit 0)。 并删除4265「未欠计数 / 当前唯一有效完整值」类承诺表述,4265 / 1049降为历史并行观测。四处(frontmatter 状态行、状态总览表 Phase 2 行、Phase 2 执行清单目标快照、本决策日志)语义一致,runtime-permission-modes.md复审轮 #6 的「s00 最终账本」段同步改为同一分栏结构。
- 该次并行观测值(canonical 原样命令,计数随 force-exit 竞态逐次浮动):
- 限制(不掩饰):本 run 为 headless 纯文档轮,未自行复跑 canonical——两组 footer 来源均明确标注为 Codex reviewer 前台 clean-run,Claude 侧不改写、不声称自跑。
- 验证:docs-only 改动,按派发要求
--no-verify前台提交;四处语义一致人工核对通过;private-marker 双面 grep(diff + commit message)0 命中。未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。
- P2 / s00+s11 根因:复审轮 #7 把
- 2026-07-18(Phase 2 复审轮 #7 = s00/s11 测试账本最终收口,纯文档轮,受控派发,仅改
runtime-permission-modes.md+ 本计划两份 md,未碰任何代码 / 测试 / canonical 命令):- 背景:复审轮 #6 的两条代码 finding(F1 官方 effort allowlist 事实修正 + 精确 token 匹配、F2 两类 toast 的 code/reason 驱动 en/zh 本地化)已在 commit
fb53dfe落地,本轮不重复处理。遗留只有 F3 —— s00 的真实 footer 与 s11 的三处一致。 - P2 / s00+s11 根因:账本里同时存在
d3e7041+4186、03ca9b0+4241/1045、a7c6795+4235/1042 三套计数,且 Phase 2 执行清单「目标快照」停在03ca9b0+4241/1045、frontmatter/状态表停在 4235/1042,与最终实现 commit 不对应;同时 headless run 的 shell 权限门禁不放行HOME=... CODEX_DISABLED=1env 前缀命令,Claude 侧无法自行产出原样 canonical footer。 - 改法(footer 来源=operator 前台 clean-run):由 operator 在本 worktree HEAD
fb53dfe上、以全新空 HOME、前台原样执行 canonical 命令(HOME=/tmp/codex-i31-home CODEX_DISABLED=1 node --test-force-exit --import tsx --test --import ./src/__tests__/db-isolation.setup.ts src/__tests__/unit/*.test.ts)跑通一次,真实 TAP footer 原文 =# tests 4265 / # suites 1049 / # pass 4265 / # fail 0 / # cancelled 0 / # skipped 0 / # todo 0 / # duration_ms 8327.997333(exit 0)。〔复审轮 #8 更正:该 footer 当时被记为「完整计数 / 当前唯一有效值」,此定性已撤回——它只是一次并行观测值,见下方复审轮 #8。〕该 footer 原文回写三处:Phase 2 执行清单「目标快照」、frontmatter 状态行 + 状态总览表 Phase 2 行、本决策日志条目,统一锚定 commitfb53dfe;runtime-permission-modes.md复审轮 #6 条目追加「s00 最终账本」段,把 4186 / 4241 由主导表述降为历史钉数(冷/暖 force-exit 竞态的演变说明保留,不删)。 - 计数环境差异说明(如实记录):
fb53dfe相对d3e7041的 +79 tests / +17 suites 全部来自共享 canonical 套件在 model-capability Phase 2 各轮累积的新增用例,非环境差异;CODEX_DISABLED单变量对照此前已两次证明单独不改变计数;并行--test-force-exit冷 HOME 首跑可能因 worker flush 竞态出现不确定欠计数(曾观察 4203/4223/4234/4238,亦有更高计数)。〔复审轮 #8 更正:原句「本次 operator 前台 clean-run 未欠计数」已撤回——串行对照证明 4265 同样欠计。〕 - 限制(不掩饰):本 run 为 headless,未自行复跑 canonical——footer 来源明确标注为 operator 前台 clean-run,Claude 侧不改写、不声称自跑。
- 验证:docs-only 改动,pre-commit 走 docs 快路(本轮按派发要求
--no-verify前台提交);private-marker 双面 grep(diff + commit message)0 命中。未做:Anthropic key × sonnet-5 真实凭据 smoke 仍留 Smoke Ledger 📋,移交用户。
- 背景:复审轮 #6 的两条代码 finding(F1 官方 effort allowlist 事实修正 + 精确 token 匹配、F2 两类 toast 的 code/reason 驱动 en/zh 本地化)已在 commit
- 2026-07-17(审查裁决):接受 P2-3/P2-4——Phase 2 必须显式裁决
agent-loop.ts:408-425的 Native effort 丢弃逻辑,EffortSelectorDropdown 五档硬编码回退列为伪档位来源进 Phase 0。P2-2 部分接受——Auto 定义为 CodePilot"不显式指定"语义;"Auto 默认落 Max"有 K3 文档 null/undefined → max 依据,但官方页面间存在版本描述冲突,渠道能力与 Auto 实际落点以 live smoke 定案;effort 下发链路不预设 env-only,需验证queryOptions.effort与CLAUDE_CODE_EFFORT_LEVEL优先级。Moonshot provider 改名明确 out-of-scope。展示决策不变。