Skip to content

G4|Feature Flags:有效开关来自注册表,不是一份 [features] ​

先别把 config.toml 当成产品目录 ​

读完 B5,下一件错事是打开 [features],按表里的 true/false 猜「仓库里有什么」。然后 multi_agent 被理解成协作模式,network_proxy = true 被理解成已经在代理流量,collaboration_modes = false 被理解成能关掉 /plan。

判断先说清楚:codex-features 是单一注册表。 每个能力有稳定 key、Stage、出厂 default_enabled。你在 toml 里写的只是叠加层。effective set = 出厂 default → 分层 TOML / legacy 别名 → CLI overlay → 依赖归一 → 企业 ManagedFeatures 钉死。B5 的 /status 和 codex features 是这面镜子;这一章讲镜子后面的表。


1. 注册表即契约:Stage 决定谁看得见 ​

Stage 五种(features/src/lib.rs:44-60):UnderDevelopment / Experimental / Stable / Deprecated / Removed。FEATURES 表把每个 Feature 钉死:id、key、阶段、默认开不开。

阶段不是注释。UnderDevelopment 不该出现在用户 toml 的「随便开」清单里;Experimental 走 /experimental 和 codex features;Removed 的键还在,是为了旧配置别炸——CollaborationModes 就是 Removed 且恒开(:1645-1648),TUI collaboration_modes_enabled() 直接返回 true。你在 config 里写 false,开关已经不存在了。

几个会读歪的出厂值:

FeaturekeyStage默认
Collabmulti_agentStable开(子代理工具门闩,D5)
MultiAgentV2multi_agent_v2Stable关
NetworkProxynetwork_proxyExperimental关(G6)
GoalsgoalsStable开
CodeModecode_modeExperimental关
CollaborationModescollaboration_modesRemoved恒开

没有注册表会怎样?每个 crate 自己 if cfg,legacy 键和现名各长各的,企业无法钉死「不许开代理」。抄开关先抄这张表,不要抄一份营销功能清单。


2. 合并顺序:后面的 overlay 赢,企业最后可以锁死 ​

Features::with_defaults() 先铺出厂值。from_sources()(约 :651-683)再叠:用户/项目/profile 的 [features]、legacy 键、CLI --enable / --disable。然后 normalize_dependencies 把「开了 A 必须带上 B」收干净。最后 ManagedFeatures(core/src/config/managed_features.rs:151-166)按企业 feature_requirements 钉死——管理员不许的,你在 toml 里写 true 也进不了 effective set。

配置层本身仍是 B5 那条 overlay 链。features 只是其中一组 key。项目层就算被信任,也进不了 denylist 里的路由/凭证键;feature 能否被项目打开,还要看 requirements。

没有这层顺序会怎样?你以为关掉了 unified_exec,企业或依赖归一又给开回来;或者项目 .codex/config.toml 偷偷打开 Experimental,clone 即生效。有效开关必须能指出 是哪一层赢的。


3. codex features 只是写用户 config 的糖 ​

CLI codex features list|enable|disable(cli/src/main.rs 里 Features 子命令)改的是用户 CODEX_HOME/config.toml,不是内存里另建一张表。--enable / --disable 是 SessionFlags,优先级更高,仍然要过同一条 from_sources。管理员 requirements 可以不许你打开某项——命令成功写了文件,effective 仍是关。

B5 已经说过:这三面镜子(features CLI、codex doctor、/status)都不拥有真相。G4 补一句:写开关和读有效值不是同一条路。 写进用户层,读的时候还要折企业层。

没有「只是糖」会怎样?你会以为 codex features disable multi_agent 立刻拆掉 D5 工具,却忘了下一层 overlay 或 Removed 键根本不听。


4. /experimental 走发现 RPC,不是第二份注册表 ​

TUI /experimental 弹窗走 app-server 的实验功能 RPC。它列出的是 当前身份下允许切换的实验项,不是 FEATURES 表全文。服务端可以按套餐、requirements、Stage 过滤。勾选之后仍写回配置层,再走第 2 节的合并。

没有「发现面」会怎样?要么把 UnderDevelopment 全摊给用户,要么每个前端自己维护一份「实验清单」,和注册表漂移。UI 是过滤后的投影。


5. 扁平 bool 和嵌套表是双入口 ​

[features] network_proxy = true 是扁平键。[features.network_proxy] 里还有 mode、credential_broker、domain 表(feature_configs.rs 的 FeatureToml)。两种写法收成同一套迭代。G6 的代理、D5 的 multi_agent_v2 嵌套槽位,都是这个双入口的消费者,不是又一套 ini。

legacy 键在 legacy.rs:11-52:collab → Collab,web_search → WebSearchRequest,telepathy → Chronicle,experimental_use_unified_exec_tool → UnifiedExec。旧文档和旧 toml 能打开新 Feature,新代码只认新 id。

没有双入口会怎样?嵌套配置找不到扁平开关,代理「开了」但 mode 丢失;或者旧 collab = true 静默失效,子代理工具突然消失。


6. 结语:带走一句话 ​

抄开关要抄注册表和合并链——Stage 决定谁看得见,企业可以锁死,CLI 和 /experimental 只是写配置的糖——不要把一份 [features] 当成产品目录。