Skip to content

Core与Core-API边界

分析 codex-core 与 codex-core-api 的实现、重导出和宿主集成边界,并对照 App Server、CLI、TUI 与示例程序的实际依赖关系。

基于rust-v0.150.0
CodexRustAPIArchitecture

Core与Core-API边界 ​

codex-core 与 codex-core-api 很容易被理解成“内部实现 crate”和“稳定 SDK crate”。源码呈现的实际 边界更细:codex-core-api 是一个面向 ThreadManager 宿主的编译期 facade,它用 pub use 汇集 Core 运行时、配置、认证、环境、协议和 extension 装配所需的类型,但不实现另一套运行时,也不提供 JSON-RPC transport。

当前 workspace 中,thread-manager-sample 是唯一直接消费 codex-core-api 的 member;App Server、 CLI、exec 和多种内建 extension 仍直接依赖 codex-core。TUI 则走另一条边界:CI 明确禁止它导入 Core,要求通过 App Server protocol/client 交互。因此这里至少存在三种不同的“公共”含义:Rust 宿主 facade、仓库内部 crate API,以及跨进程 wire protocol。

阅读本文前,建议先了解 Core运行时架构总览 中 ThreadManager 与 CodexThread 的职责。本文只研究 Rust 依赖与可见性边界,不把 facade 编译成功解释成语义稳定、进程 隔离或 wire compatibility。

读完后,应能判断一个新类型应由 owning crate、core-api、extension-api 还是 App Server protocol 公开,并能用 consumer build 与边界脚本区分“导出面可编译”和“运行时行为正确”。

1. 三层边界 ​

边界载体主要消费者兼容性表现
Core 内部实现边界codex-core 的 pub / pub(crate)Core 本身及仓库内直接依赖者Rust 编译期类型和方法
宿主装配边界codex-core-api 的重导出thread-manager-sample、潜在嵌入方单一 crate import surface
产品协议边界codex-app-server-protocolTUI、SDK、IDE/桌面客户端JSON-RPC、schema 和 wire compatibility

core-api 的“API”不等于 HTTP 或 JSON API。调用方与 Core 运行在同一进程,共享同一个 Arc<ThreadManager> 和 Tokio runtime。需要进程隔离、版本协商或跨语言调用时,正确边界是 App Server protocol,而不是尝试把 Rust ThreadManager 序列化出去。

图中 App Server 没有经过 core-api,这是当前源码事实,不是漏画。它需要 thread state、extension event sink、request processor 和 Core 内部配置能力,因此仍以仓库内部产品 crate 的身份直接依赖 codex-core 与其他组件。

2. core-api ​

core-api/src/lib.rs 只有模块说明、lint 和 pub use。当前版本共有 131 条 pub use,没有定义自己的 public struct、enum、trait 或 function:

源码位置:codex-rs/core-api/src/lib.rs :: public re-export facade

rust
//! Public facade for thread management APIs built on `codex-core`.

#![deny(private_bounds, private_interfaces, unreachable_pub)]

// Thread 生命周期的主入口仍由 codex-core 实现。
pub use codex_core::CodexThread;
pub use codex_core::ForkSnapshot;
pub use codex_core::NewThread;
pub use codex_core::StartThreadOptions;
pub use codex_core::ThreadManager;
pub use codex_core::ThreadShutdownReport;
pub use codex_core::UserMessageAdmission;
pub use codex_core::WaitForEnvironmentToolConfig;
pub use codex_core::build_models_manager;
pub use codex_core::init_state_db;
pub use codex_core::local_agent_graph_store_from_state_db;
pub use codex_core::resolve_installation_id;
pub use codex_core::skills::HostSkillsService;
pub use codex_core::thread_store_from_config;

// 宿主构造运行时所需的配置、认证、模型和环境类型来自各自 crate。
pub use codex_core::config::Config;
pub use codex_core::config::Constrained;
pub use codex_core::config::ExtraConfig;
pub use codex_exec_server::EnvironmentManager;
pub use codex_exec_server::EnvironmentRegistryConnectRequest;
pub use codex_exec_server::EnvironmentRegistryConnectResponse;
pub use codex_exec_server::ExecServerError;
pub use codex_extension_api::ExtensionRegistryBuilder;
pub use codex_extension_api::UserInstructionsProvider;
pub use codex_features::Feature;
pub use codex_features::Features;
pub use codex_login::AuthManager;
pub use codex_models_manager::manager::SharedModelsManager;

// facade 同时公开宿主驱动 Thread 所需的协议信封与稳定标识。
pub use codex_protocol::ThreadId;
pub use codex_protocol::config_types::ApprovalsReviewer;
pub use codex_protocol::error::Result as CodexResult;
pub use codex_protocol::models::PermissionProfile;
pub use codex_protocol::protocol::AskForApproval;
pub use codex_protocol::protocol::EventMsg;
pub use codex_protocol::protocol::InitialHistory;
pub use codex_protocol::protocol::Op;
pub use codex_protocol::protocol::SessionConfiguredEvent;
pub use codex_protocol::protocol::SessionSource;
pub use codex_protocol::protocol::TurnEnvironmentSelection;
pub use codex_protocol::protocol::W3cTraceContext;
pub use codex_protocol::user_input::UserInput;

// App Server 通知被导出,但 facade 没有实现 JSON-RPC transport。
pub use codex_app_server_protocol::ServerNotification;
pub use codex_app_server_protocol::item_event_to_server_notification;
pub use codex_state::SqliteConfig;
pub use codex_utils_absolute_path::AbsolutePathBuf;

这产生三个直接结论:

  1. codex_core_api::ThreadManager 与 codex_core::ThreadManager 是同一个 Rust 类型;
  2. 调用 facade 不增加 wrapper、转换或动态分派开销;
  3. 行为、错误和生命周期仍完全由来源 crate 实现。

pub use 只改变名称解析路径。ThreadManager::start_thread() 的方法集合不会因为从 core-api 导入 而变化,CodexErr 也不会被 facade 包装成另一种错误。如果底层类型的 public signature 改变, facade 消费者会直接感受到源码级变化。

2.1 lint导出面 ​

lint防止的问题对 facade 的意义
private_boundspublic item 的 generic bound 使用私有类型宿主不能满足不可见约束
private_interfacespublic signature 暴露更私有的参数或返回类型导出后无法实际调用
unreachable_pub声明为 public 却无法从 crate root 到达防止伪公共 API

这些 lint 保证 Rust 可见性自洽,但不定义语义稳定性。源码没有因为加上 pub use 就自动产生弃用 周期、版本协商或 wire compatibility;这些仍需要单独的 API 演进规则。

2.2 facade测试 ​

core-api/Cargo.toml 明确配置:

源码位置:codex-rs/core-api/Cargo.toml :: [lib]。

toml
# facade 只做编译期重导出,因此关闭自身 test/doctest target。
[lib]
doctest = false
name = "codex_core_api"
path = "src/lib.rs"
test = false

因此它不会生成独立 unit-test 或 doctest binary。边界主要通过三种方式验证:facade crate 自身能否 编译、thread-manager-sample 能否只依赖它完成装配,以及下游 Cargo/Bazel target 能否继续构建。 行为测试仍属于 codex-core 或具体产品,因为 facade 没有要单独执行的行为。

3. core-api 汇集 ​

如果只重导出 ThreadManager 和 CodexThread,宿主仍无法创建它们。构造 manager 需要配置、认证、 模型目录、environment manager、thread store、extension registry 和 installation identity。因此 core-api 同时依赖并重导出多个 owning crate 的类型。

导出组代表导出来源 crate宿主用途
Thread 运行时ThreadManager、CodexThread、StartThreadOptions、ForkSnapshotcodex-core创建和控制 thread
Core 配置Config、Permissions、ThreadStoreConfig、Constrainedcodex-core形成可执行配置
配置数据ConfigLayerStack、History、TUI/realtime/OTel config typescodex-config填充完整 Config
认证与 providerAuthManager、CodexAuth、built_in_model_providerslogin/model crates准备模型身份与目录
执行环境EnvironmentManager、ExecServerRuntimePaths、registry request/responsecodex-exec-server装配 local/remote executor
ExtensionExtensionRegistryBuilder、user-instruction provider、image extension installerextension/home crates构造 host extension registry
Core wire inputOp、EventMsg、UserInput、PermissionProfilecodex-protocol提交 Turn 和读取事件
产品通知辅助ServerNotification、item_event_to_server_notificationapp-server protocol将部分 Core event 映射成产品通知
状态与路径SqliteConfig、StateDbHandle、AbsolutePathBufstate/core/utils准备持久化与 cwd

这使 core-api 更像 host composition prelude,而不是严格意义上的最小 interface crate。它直接依赖 17 个 workspace package;链接它仍会把 codex-core 及其运行时依赖带入构建。facade 减少的是 import 和 dependency declaration 的分散,不是最终二进制体积。

下面的类图按来源 crate 展示 facade 的类型构成。core-api 没有复制这些类型,只把宿主需要的入口 集中到一个导入面。

调用方从 codex_core_api::Op 导入的仍是 codex_protocol::Op,从 facade 获取 ThreadManager 也仍执行 codex-core 的实现。重导出改变路径,不改变 owner 或错误来源。

4. 导出与隐藏共同定 ​

理解 facade 不能只列“有什么”,还要看“故意没有什么”。当前 core-api 没有重导出:

  • Session、SessionIo、SessionState、ActiveTurn、TurnState;
  • TurnContext、StepContext;
  • ToolRegistry、ToolRouter、具体 tool handler/runtime;
  • ModelClient、ModelClientSession、Prompt、ResponseStream;
  • RolloutRecorder 的完整管理面和 Core test support;
  • App Server 的 request processor、transport 和 client request schema。

这些缺口形成了有意义的约束:嵌入方通过 ThreadManager/CodexThread + Op/EventMsg 操作 agent,不能 直接替换 active task、持有 TurnState lock 或调用内部 tool handler。模型采样和工具循环仍是 Core 的不透明实现。

不过这不是 capability sandbox。Rust 可见性只限制编译时访问;宿主仍与 Core 在同一进程,拥有相同 OS 用户权限。core-api 不能隔离崩溃、内存、文件访问或第三方 extension,安全边界仍由配置、审批和 平台 sandbox 提供。

5. Sample Facade ​

thread-manager-sample/Cargo.toml 对依赖有一条明确规则:

源码位置:codex-rs/thread-manager-sample/Cargo.toml :: [dependencies]。

toml
# sample 只依赖 facade,用构建失败暴露 core-api 导出面的缺口。
# Keep this sample limited to a single Codex workspace dependency.
# Add new Codex surface area to `codex-core-api` instead of depending on
# additional `codex-*` crates here.
codex-core-api = { workspace = true }

这使 sample 同时承担两个角色:演示最小同进程宿主的装配顺序,并暴露 facade 缺失。如果实现一个 基本 Turn 必须在 sample 中新增第二个 codex-* dependency,维护者应先判断该能力是否属于 core-api 的宿主 surface。

5.1 Sample装配 ​

示例配置 read-only PermissionProfile 和 AskForApproval::Never,启动 state DB、AuthManager、 EnvironmentManager、ThreadStore 与 image-generation extension,然后创建 ThreadManager。它提交一次 Op::UserInput,读取事件直到 TurnComplete,对需要审批、权限或用户输入的事件直接报错,最后显式 shutdown 并从 manager 移除。

下面是 sample 的真实装配与清理链。所有名称都从 codex_core_api 导入,但构造的 owner 仍是各来源 crate 的原始类型。

源码位置:codex-rs/thread-manager-sample/src/main.rs :: run_main(装配与关闭节选)

rust
let config = new_config(args.model, arg0_paths)?;
let state_db = init_state_db(&config).await;

let auth_manager =
    AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await;
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
    config.codex_self_exe.clone(),
    config.codex_linux_sandbox_exe.clone(),
)?;
let thread_store = thread_store_from_config(&config, state_db.clone());
let environment_manager = Arc::new(
    EnvironmentManager::from_codex_home(
        config.codex_home.clone(),
        Some(local_runtime_paths),
        config.http_client_factory(),
    )
    .await?,
);
let installation_id = resolve_installation_id(&config.codex_home).await?;
let user_instructions_provider = Arc::new(CodexHomeUserInstructionsProvider::new(
    config.codex_home.clone(),
));
let mut extensions = ExtensionRegistryBuilder::<Config>::new();
install_image_generation_extension(&mut extensions, auth_manager.clone(), |config: &Config| {
    Some(config.codex_home.clone())
});

// facade提供构造所需全部类型,sample无需第二个codex-* workspace dependency。
let thread_manager = ThreadManager::new(
    &config,
    Arc::clone(&auth_manager),
    build_models_manager(&config, auth_manager),
    CodexAppsToolsCache::default(),
    SessionSource::Exec,
    environment_manager,
    Arc::new(extensions.build()),
    user_instructions_provider,
    /*analytics_events_client*/ None,
    Arc::clone(&thread_store),
    local_agent_graph_store_from_state_db(state_db.as_ref()),
    installation_id,
    /*attestation_provider*/ None,
    /*external_time_provider*/ None,
);

let NewThread {
    thread_id, thread, ..
} = thread_manager
    .start_thread(StartThreadOptions::new(config))
    .await
    .context("start Codex thread")?;

let thread_id_string = thread_id.to_string();
let turn_output = run_turn(&thread, &thread_id_string, prompt).await;

// facade不会替宿主管理生命周期:先等Session关闭,再移除manager registry引用。
let shutdown_result = thread.shutdown_and_wait().await;
let _ = thread_manager.remove_thread(&thread_id).await;

turn_output?;
shutdown_result.context("shut down Codex thread")?;

事件循环同样由宿主负责选择策略。sample 对需要双向交互的事件 fail fast,而不是 facade 自动批准; TurnComplete、Core error 与用户中断也保持不同终态。

源码位置:codex-rs/thread-manager-sample/src/main.rs :: run_turn(终止分支节选)

rust
match event.msg {
    EventMsg::TurnComplete(_) => {
        return Ok(());
    }
    EventMsg::Error(event) => {
        bail!(event.message);
    }
    EventMsg::TurnAborted(_) => {
        bail!("turn aborted");
    }
    // sample没有交互UI,遇到审批/权限请求时明确失败,不代替宿主作安全决定。
    EventMsg::ExecApprovalRequest(_) => {
        bail!("turn requested exec approval");
    }
    EventMsg::ApplyPatchApprovalRequest(_) => {
        bail!("turn requested patch approval");
    }
    EventMsg::RequestPermissions(_) => {
        bail!("turn requested permissions");
    }
    EventMsg::RequestUserInput(_) => {
        bail!("turn requested user input");
    }
    EventMsg::DynamicToolCallRequest(_) => {
        bail!("turn requested a dynamic tool call");
    }
    _ => {}
}

这条路径说明 facade 提供的是原语,不是高层“一次调用返回字符串”SDK。宿主必须选择审批行为、持续 消费事件、响应交互请求、处理 terminal event,并负责关闭顺序。

5.2 手工Config ​

Sample 为了保持单一依赖,直接填充完整 Config struct。它没有通过 core-api 使用 ConfigBuilder,因为 facade 当前未导出 builder。这让示例很明确,但也意味着配置新增 public 字段 可能造成 sample 的源码编译变化。

因此不能仅凭 core-api 这个名字推断它已经是小而稳定的外部 SDK。当前更准确的描述是:它是一份由 可执行 sample 驱动的 curated embedding surface,仍然贴近 workspace 内部类型。

6. 当前消费者 ​

6.1 App Server ​

App Server 直接依赖 codex-core,还同时依赖 codex-core-plugins、codex-exec-server、 codex-extension-api、protocol、state、thread-store 等 crate。它需要的能力超过 sample:

  • request processor 按 RPC 管理多个连接和 thread;
  • 自定义 extension event sink,把事件发给订阅连接;
  • 组装 Goals、Guardian、Memories、MCP、Web Search、Skills 等 extension;
  • 管理 skills watcher、MCP refresh、attestation 和 live thread state;
  • 使用部分尚未进入 facade 的配置与 runtime 类型。

所以 App Server 当前的边界是“内部产品依赖 Core”,而不是“只依赖 core-api 的外部嵌入方”。如果未来 迁移,也需要先把实际所需能力变成清晰的宿主 contract,不能只批量复制 pub use。

6.2 CLI依赖 ​

CLI/exec 与 Core 同属交付二进制内部,使用登录、sandbox debug、配置、shell、rollout 查找和测试辅助 等更宽表面。让它们改用 facade 只有在希望强制收窄依赖时才有价值;单纯替换 import path 不会改变 运行时架构。

6.3 Extension依赖 ​

内建 ext/agent、Goal、Guardian、image generation、MCP、Memories 和 Web Search 等 crate 当前直接 依赖 codex-core,常见原因是需要 Config、ThreadManager 或 Core context fragments。这些依赖属于 同一 workspace 的编译期集成,不应被误认为第三方 extension ABI。

当 extension 只需要 contributor trait 时,应优先依赖 extension-api;只有确实需要 Core host 类型 时才向 Core 扩张。否则 extension 与 Core 容易形成反向耦合,最终把新概念再次推回最大的 crate。

6.4 TUI边界 ​

.github/scripts/verify_tui_core_boundary.py 同时检查 manifest 和 Rust 源码:禁止 codex-core dependency,也禁止 codex_core::、use codex_core 和 extern crate codex_core。失败信息要求使用 App Server protocol/client;临时 embedded startup 缺口只能放在 app-server-client 的 legacy_core 后面。

这条 CI 规则比命名约定更强:TUI 不能因为需要一个方便的 Config 或 Thread 类型就绕过服务边界。 因此 core-api 也不是 TUI 的替代路径;否则会把同进程 Core 类型重新引入 UI。

7. 依赖层选择 ​

新代码所在位置首选依赖原因
同进程 ThreadManager 嵌入示例/宿主codex-core-api单一 curated composition surface
App Server 内部 processorcodex-core + 最小 owning crates当前产品内部集成,需要更宽能力
TUI、SDK、跨语言客户端app-server protocol/client需要 wire schema、隔离和版本协商
通用 extension contributorcodex-extension-api不应反向依赖整个 Core
Core 内部实现模块私有/pub(crate)避免无意扩大外部 surface
低层可复用库具体 owning crate不应通过 facade 反向拉入 Core

判断一个类型是否应加入 core-api,至少回答四个问题:

  1. ThreadManager 宿主是否必须直接构造或读取它?
  2. 能否用现有 Op/EventMsg 或 builder 隐藏该内部细节?
  3. 导出后是否迫使 facade 新增一个重量级 dependency?
  4. 它属于同进程 Rust API,还是本应进入 App Server wire schema?

如果答案只是“App Server 某个私有 processor 用起来方便”,不应加入 facade。如果跨语言客户端需要该 能力,也不能只做 Rust re-export;必须定义 protocol 类型、serialization、experimental gating 和 schema fixture。

8. pub use边界 ​

一个类型从 Core 内部细节变成 facade 契约,必须经过显式选择和下游验证。下面的状态机表示 API surface 的演进阶段;pub use 一旦被 sample 或外部宿主采用,就不能再按普通内部重构处理。

Rejected 回到内部 owner,并不表示类型设计失败;它只说明该能力不属于 curated embedding surface。 真正进入 Consumed 后,删除或签名变化都需要迁移与验证链。

facade 变化可能产生三类影响:

变化影响面典型验证
新增 re-exportfacade source surface 扩大core-api 与 sample 编译
删除/重命名 re-export嵌入方 import 直接失败sample/下游 compile error
底层类型字段或方法变化type identity 不变,但调用代码可能失败owning crate tests + sample build
Core 行为变化编译可能正常,运行语义改变Core integration tests
App Server wire 变化与 facade 无直接等价关系schema diff + App Server tests

特别需要区分“源码兼容”和“行为兼容”。ThreadManager 方法签名没变,不代表 resume、fork、审批或事件 顺序没变;这些由 Core 测试保证。反过来,App Server 新增一个 optional wire field,也不要求 core-api 同名导出任何东西。

9. 边界修改 ​

core-api 没有独立测试 binary,因此验证应从 surface、composition 和 behavior 三层展开:

  1. Surface:cargo check -p codex-core-api 或对应 Bazel build,确保所有 re-export 可达且 lint 通过;
  2. Composition:构建 codex-thread-manager-sample,确保它仍只有一个 Codex workspace dependency;
  3. Behavior:修改来自 codex-core 的类型或方法时,运行所属 Core crate/integration tests;
  4. Product:若 App Server/TUI 同时受影响,分别运行公共 JSON-RPC 和 UI snapshot tests;
  5. Protocol:wire 类型变化必须重新生成并比较 stable/experimental schema。

边界审查还应查看 Cargo 依赖方向。低层 crate 如果为了一个 config enum 改为依赖 core-api,会间接把 整个 Core 拉入依赖图,方向是错误的;它应该依赖 codex-config 或 codex-protocol 中真正拥有该类型 的 crate。

最终可以用一句话概括这两层:codex-core 拥有运行时实现和仓库内部 public surface; codex-core-api 为同进程 ThreadManager 宿主重导出一组可装配原语。它收窄 import 边界,但不复制 运行时、不形成安全隔离,也不替代 App Server 的跨进程协议。

10. 导出面实测 ​

core-api 关闭了自己的 unit-test/doctest target,所以最有价值的边界验证不是“运行 facade 测试”,而是 让真实 consumer 完成类型检查,并让仓库规则检查不允许绕过另一条产品边界。以下实验在 Darwin arm64、Rust/Cargo 1.96.0 上执行。

10.1 Facade导出 ​

在 codex-rs/ 运行:

bash
cargo check -p codex-core-api -p codex-thread-manager-sample

结果为成功,Cargo 最后检查了 codex-core、codex-core-api 和 codex-thread-manager-sample,并输出:

text
Checking codex-core-api v0.150.0 (.../codex-rs/core-api)
Checking codex-thread-manager-sample v0.150.0 (.../codex-rs/thread-manager-sample)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 30s

这项结果直接证明两件事:所有 pub use 在当前 target 可达;sample 只通过 facade 导入的构造类型能够完成 类型检查。它不执行模型请求、Thread 生命周期或事件循环,因此不能证明 resume、approval、shutdown 等 行为;这些仍属于 codex-core 的测试责任。

10.2 导出与consumer ​

正文中的“131 条 pub use”和“唯一 workspace consumer”不是手工估计,可用下面的只读命令重建:

bash
# 统计facade导出语句。
rg -n '^pub use ' codex-rs/core-api/src/lib.rs | wc -l

# 找出真正声明core-api依赖的member manifest;根Cargo.toml注册不属于consumer。
rg -l '^codex-core-api\s*=|^codex-core-api\.' \
  codex-rs/*/Cargo.toml codex-rs/ext/*/Cargo.toml

# 验证sample只有一个codex-* workspace dependency。
rg -n '^codex-[a-z0-9-]+\s*=.*workspace = true' \
  codex-rs/thread-manager-sample/Cargo.toml

当前输出分别是 110、codex-rs/thread-manager-sample/Cargo.toml,以及唯一一行 codex-core-api = { workspace = true }。这类 inventory 结论会随 tag 改变,升级时必须重跑,不能把数字 当成永久架构常量。

10.3 TUI边界验证 ​

实际执行下面的仓库脚本返回 0,且没有输出 failure:

bash
python3 .github/scripts/verify_tui_core_boundary.py

脚本不是简单搜索一个 Cargo section。它遍历普通、dev、build 和 target-specific dependencies,并扫描 全部 Rust 文件的三种直接导入形状。

源码位置:.github/scripts/verify_tui_core_boundary.py

python
FORBIDDEN_PACKAGE = "codex-core"
FORBIDDEN_SOURCE_PATTERNS = (
    re.compile(r"\bcodex_core::"),
    re.compile(r"\buse\s+codex_core\b"),
    re.compile(r"\bextern\s+crate\s+codex_core\b"),
)

def main() -> int:
    failures = []
    # manifest依赖和源码import必须同时为零,才接受TUI边界。
    failures.extend(manifest_failures())
    failures.extend(source_failures())

    if not failures:
        return 0

    print("codex-tui must not depend on or import codex-core directly.")
    return 1

这个规则证明 TUI 当前没有直接绕过 App Server 边界,但它没有禁止所有“间接依赖图中最终出现 Core”的 可能,也不检查任意第三方 crate;它是针对 codex-tui manifest 和源码的仓库约束。

10.4 隐藏类型验证 ​

当前仓库没有 trybuild 或独立 downstream compile-fail fixture,专门写 use codex_core_api::Session;、TurnState 或 ToolRouter 并断言编译失败。本文关于这些类型未导出的结论 来自 export inventory 与 Rust 名称解析规则;正向 sample build 只证明“应该有的 surface 足够”,没有反向 证明“所有不该有的名字都被长期锁死”。

如果未来要把隐藏面也变成自动契约,应新增 compile-fail fixture,并只选择少量架构关键类型;否则把 全部未导出符号做成快照,会让正常内部新增也造成无意义维护成本。

11. Facade边界 ​

可以按下面顺序验证边界,不需要先读完整个 Core:

bash
# 1. facade究竟定义行为,还是只重导出?
rg -n '^pub (struct|enum|trait|fn|mod)|^pub use ' codex-rs/core-api/src/lib.rs

# 2. sample是否绕过facade依赖其他codex crate?
rg -n '^codex-' codex-rs/thread-manager-sample/Cargo.toml

# 3. App Server与TUI分别选择了哪条边界?
rg -n '^codex-core\s*=|codex_core::|codex_app_server' \
  codex-rs/app-server codex-rs/tui

完成检查后,应能回答:

  1. 为什么 codex_core_api::ThreadManager 与 codex_core::ThreadManager 具有同一类型身份?
  2. 为什么 sample 编译成功仍不能证明 Thread 的关闭和事件顺序正确?
  3. 为什么 TUI 不应该改为依赖 core-api 来获取一个方便的 Core 类型?
  4. 什么情况下应扩充 facade,什么情况下应新增 App Server wire type?

继续学习运行时行为时,可阅读 ThreadManager创建 和 CodexThread公共API;它们分别承担 facade 编译实验不能覆盖的创建事务 与生命周期语义。