diff --git a/docs/architecture/product-architecture.md b/docs/architecture/product-architecture.md index 9c171546ba..b475bc1249 100644 --- a/docs/architecture/product-architecture.md +++ b/docs/architecture/product-architecture.md @@ -820,6 +820,7 @@ flowchart LR | Desktop | 使用 `product-full`;Settings 从现有来源目录和 integration policy 生成简短应用概览,具体审批与冲突仍进入 Tool、Agent、MCP 或 Hook owner | 可执行能力在事实所在 Host 运行;Safe Mode 只阻止新调用,不改来源、不取消正在运行的调用 | | CLI / TUI | 使用显式 Core owner closure:`agent-runtime` 基线、实际 service owner(包括 Remote Connect、DeepResearch、LSP、external/plugin source 与 SSH)以及九组 `tools-*`;`/extensions` 只提供状态、启停和刷新,`/hooks`、`/tools`、`/agent` 和 `/mcp` 处理各自能力 | `agent-runtime` 不再隐式携带完整 MCP/Remote/Browser/Web/Git/LSP/模型目录闭包;非交互不等待权限输入,生态解析仍在适配器,远程能力未接入时不回退本机 | | ACP | 使用 `DeliveryProfile::Acp`、Runtime Parts、`agent-runtime` 基线、所需 service owner 与九组 `tools-*`,但不选择 CLI 的 plugin runtime 和 Remote Connect owner | load 成功后才发布活动状态;close 排空后再卸载;完整历史、Canvas 工具物化、兼容指令来源和配置仍由 Core/ACP 管理;未选择的能力不得借 Cargo feature union 偶然出现 | +| SDK Host(preview) | 使用 `DeliveryProfile::Sdk`、Runtime Parts 和与当前本机协议能力一致的显式 Core owner closure;TLS provider 由 Host 进程入口安装 | 当前协议不暴露远程 workspace/SSH 执行,因此不选择 Remote Connect、SSH 或 Function Agent owner;未来远程 SDK 必须复用 Server/Remote 的认证和执行域,不能回退到本机执行 | | Peer / Server | Peer Host 执行真实工作区操作;通用 HTTP Server 未绑定可信 workspace owner 时明确返回不支持 | 控制端不替远端发现或执行;loopback 单用户边界不扩展到远程/多用户;SSH Remote 未接入时返回不支持 | | Web / Mobile Web | 依赖现有后端入口 | 不持有插件执行单元,也不能据空 profile 宣称独立能力 | | HarmonyOS 手机 Remote | phone-only ArkTS 远程入口 | 不等于 HarmonyOS PC 本地 Runtime、CLI/TUI 或 GUI | diff --git a/docs/performance/01-compile-performance.md b/docs/performance/01-compile-performance.md index 509ed20b22..d54213f805 100644 --- a/docs/performance/01-compile-performance.md +++ b/docs/performance/01-compile-performance.md @@ -1,8 +1,8 @@ # BitFun 编译与依赖治理计划 -> 最近核实:2026-08-10 +> 最近核实:2026-08-11 > -> 实现复核基线:`gcwing/main@734e5b05f` +> 实现复核基线:`gcwing/main@9f8b56082` > > 性能 A/B 基线:`gcwing/main@1f538b96d` > @@ -16,9 +16,10 @@ | 结论 | 说明 | |---|---| -| 服务测试链接拓扑已收敛 | Services 两个 crate 的集成 target 总数从 33 降到 25;选中的 `local-storage`、MCP、基础 SSH 闭包从 16 个集成 executable 降到 8 个 | +| 集成测试链接拓扑已收敛 | Services 两个 crate 的集成 target 总数从 33 降到 25;External Sources 的 adapter/assembly target 从 22 降到 7,进程和外部系统失败域保持独立 | | Agent Runtime 基线不再隐藏重型 capability | `bitfun-core/agent-runtime` 只保留生命周期和基础工具 owner;文档转换与订阅认证也改为产品显式 modifier。在最新主线 A/B 中,三平台 normal/build 闭包进一步减少 69/64/110 个版本化 package instance | | App Server 不继承未消费能力 | App Server 保持现有 Agent/Git/外部来源 handler 边界,不再因 Core 基线携带文档转换和本地订阅凭据,三平台闭包减少 61/56/78 | +| SDK Host 使用显式能力闭包 | SDK Host 保留当前本机协议和工具能力,但不再通过 `product-full` 携带协议未暴露的 Remote Connect、SSH、Function Agent 等能力;Windows/macOS/Linux normal/build 闭包减少 66/68/76 | | 完整产品行为和闭包保持 | `product-full` 显式组合全部 owner,Windows normal/build 闭包保持 570;CLI 保持 649。ACP 只退出未选择或未使用的隐含能力,累计在 Windows/macOS/Linux 分别减少 12/15/24 | | Installer 删除未使用的直接能力 | 独立 manifest 的直接 dependency 从 18 降到 10,Windows normal/build 闭包减少 6;不把 Installer 并入根 workspace,本 PR 按要求不提交其生成 lockfile | | focused test 仍保持精确 | 同 owner、feature、平台和进程语义的源文件进入分组 target;使用 `--test ::` 运行单模块 | @@ -45,7 +46,9 @@ ## 3. 当前基线 -### 3.1 服务层测试拓扑 +### 3.1 集成测试链接拓扑 + +#### Services 本轮只合并 owner 和运行边界相同的测试。`session_write_lock_contracts` 依赖当前测试 executable 启动异常退出子进程,因此继续保持独立;不同 feature 的服务测试也不合并。 @@ -73,6 +76,24 @@ clean/rebuild,表中为均值。时间是方向性证据,不是硬阈值。 MCP 的 2→1 candidate 也做过同口径 A/B,但冷构建和 owner 重建均无可区分的提速;streamable HTTP 测试还拥有真实 loopback TCP/SSE/超时失败域,因此最终继续保持两个 target,不计入本轮收益。 +#### External Sources adapters 与 assembly + +同一 crate 内、相同依赖和运行边界的静态来源合同通过 wrapper target 收敛;测试正文逐字迁移,仍可用 +`--test ::` 聚焦到单个来源模块。OpenCode 的 MCP 子进程、受管插件服务和 Node 脚本 +runtime 分别保留独立 target,避免为了减少链接次数混合不同环境、超时和故障语义。 + +| 范围 | 变更前 target | 变更后 target | 集成测试数 | +|---|---:|---:|---:| +| OpenCode adapter | 8 | 4 | 130 | +| Claude Code adapter | 4 | 1 | 51 | +| Codex adapter | 3 | 1 | 47 | +| External Sources assembly | 7 | 1 | 30 | +| 合计 | 22 | 7 | 258 | + +这部分只减少 15 个重复链接的 test executable;未新增 dependency、feature 或 CI 命令,也不以当前证据 +宣称 wall-clock 提速。Cargo 边界检查锁定显式 target、wrapper-only root、leaf 唯一引用和 crate-level cfg, +避免后续新增测试静默绕过分组拓扑。 + 可重复确认的产物变化如下;`test executable` 包含每个 crate 的 lib test harness,因此比 integration target 多 1。PDB 大小会随工具链变化,只比较同次 A/B: @@ -95,8 +116,10 @@ package/version,不等同于实际秒数。路径 package 因 A/B worktree 路 | Desktop | 792 → 792 | 807 → 807 | 892 → 892 | 完整产品继续使用既有跨平台截图行为,本轮不以扩大根 lock 依赖宇宙换取单平台闭包下降 | | Installer | 333 → 327 | — | — | Windows 独立 workspace;直接 dependency 18 → 10 | -在最新实现复核基线 `gcwing/main@734e5b05f` 上,本轮继续把两个重型能力从 Core 基线改为弱 -modifier。计数先移除 Cargo tree 的重复展示标记 `(*)`,再按 package/version 去重: +下表前五项延续 `gcwing/main@734e5b05f` 的已核实 A/B,SDK Host 行以 +`gcwing/main@22f5411e7` 为变更前基线。三平台 target 分别为 `x86_64-pc-windows-msvc`、 +`aarch64-apple-darwin` 和 `x86_64-unknown-linux-gnu`;计数先移除 Cargo tree 的重复展示标记 +`(*)`,再按 package/version 去重: | 本轮闭包 | Windows | macOS | Linux | 行为边界 | |---|---:|---:|---:|---| @@ -105,9 +128,12 @@ modifier。计数先移除 Cargo tree 的重复展示标记 `(*)`,再按 packa | Core `product-full` | 570 → 570 | 557 → 557 | 601 → 601 | 显式恢复 `document-read` 与 `subscription-auth` | | CLI | 649 → 649 | 649 → 649 | 672 → 672 | 显式保持原有能力 | | ACP | 589 → 587 | 574 → 572 | 594 → 592 | 保持原有能力,同时退出 Reqwest 未使用的 `mime_guess`/`unicase` | +| SDK Host | 578 → 512 | 565 → 497 | 609 → 533 | 保留本机 SDK profile、九组工具 owner、外部静态来源和 ring TLS;退出未暴露的 Remote Connect、SSH、Function Agent 与完整产品附属能力 | -本轮没有新增 crate 或第三方 dependency。收益来自两类现有重闭包退出窄入口:`anydoc` 及其 -文档解析/压缩依赖,以及订阅凭据的 keyring/加密/本地存储依赖。完整产品 package 集合不变, +本轮没有新增 crate 或第三方 package;SDK Host 只把已有测试依赖 `rustls` 调整为进程入口实际使用的 +normal dependency,根 lock package 集合不变。前两类收益来自 `anydoc` 及其文档解析/压缩依赖, +以及订阅凭据的 keyring/加密/本地存储依赖;SDK Host 的收益来自未公开远程能力对应的 +SSH、密钥和连接子图退出。完整产品 package 集合不变, 因此这里只报告依赖图收敛,不宣称 `product-full` wall-clock 提速。 Package instance 会低估“同一个大 crate 少编译了多少 feature 代码”。在 Windows @@ -122,7 +148,7 @@ Plugin Source 由各自 owner 选择,完整产品仍经 `product-full` 显式 | 状态 | 范围 | 处理结论 | |---|---|---| | 已稳定 | 根 `Cargo.lock`、Reqwest Rustls 单栈、workspace Tokio 最小基线 | 不重复治理 | -| 本轮完成 | Core Agent Runtime capability、文档转换与订阅认证 modifier、Installer 未使用直接依赖 | 以真实入口 closure 收敛,不建立新的产品 umbrella,也不扩大根依赖宇宙 | +| 本轮完成 | Core Agent Runtime capability、文档转换与订阅认证 modifier、SDK Host 显式 owner closure、Installer 未使用直接依赖 | 以真实入口 closure 收敛,不建立新的产品 umbrella,也不扩大根依赖宇宙 | | 当前不动 | App Server / Server | 只为保持现有 handler 编译显式声明其已消费的 Core owner;不在改造稳定前继续拆其生产路径 | | 明确保留 | Desktop screenshots backend | 替换方案必须同时保持三平台坐标/权限/区域捕获语义且不增加根 lock package;当前候选不满足 | | 明确保留 | `portable-pty 0.8/0.9` | 非 OHOS 与 OHOS 的平台兼容选择,不为去重破坏 | @@ -149,8 +175,10 @@ Plugin Source 由各自 owner 选择,完整产品仍经 `product-full` 显式 | Agent Runtime 闭包 | Core 基线不再暗带具体 capability;完整产品和 CLI 显式保持原能力,ACP 退出未选择闭包 | | 重型可选能力 | 文档转换和本地订阅凭据由弱 modifier 细化已有 runtime owner;Core 基线和 App Server 退出未消费闭包 | | Installer 闭包 | 删除 8 个未使用直接 dependency;独立 workspace 和发布生命周期不变,本 PR 不提交其生成 lockfile | +| SDK Host 闭包 | 从 `product-full` 改为与当前协议/构造路径一致的显式 Core owner closure;保留 ring TLS 初始化,本机 SDK 行为不变,未交付的远程执行能力不再进入构建图 | | Agent Runtime 测试 | 28 个 integration executable 已收敛为 5 个职责/平台 target | | Services 测试 | 两个服务 crate 使用显式 target;选中闭包少 8 个 integration executable,进程/feature/external-system 边界保持独立 | +| External Sources 测试 | 四个 adapter/assembly crate 从 22 个 target 收敛到 7 个;MCP、插件服务和脚本 runtime 继续独立 | 内置 Agent 内容已经移到无第三方依赖的 `bitfun-agent-content`,减少了 Core build-script 工作; 但 Core 仍直接依赖该 crate。没有足够产品收益前,不为消除这一编译指纹引入动态 provider、 diff --git a/scripts/check-core-boundaries.test.mjs b/scripts/check-core-boundaries.test.mjs index 7aa64e468e..eef315b9ab 100644 --- a/scripts/check-core-boundaries.test.mjs +++ b/scripts/check-core-boundaries.test.mjs @@ -20,9 +20,14 @@ import { } from './core-boundaries/cargo-dependency-boundaries.mjs'; import { checkCliIntegrationTestTopology, + checkExternalSourceIntegrationTestTopologies, checkServicesCoreIntegrationTestTopology, checkServicesIntegrationsIntegrationTestTopology, + claudeCodeAdapterIntegrationTestTargets, cliIntegrationTestTargets, + codexAdapterIntegrationTestTargets, + externalSourcesIntegrationTestTargets, + opencodeAdapterIntegrationTestTargets, validateExplicitIntegrationTestTopology, } from './core-boundaries/explicit-test-topology.mjs'; import { crateLayoutRules } from './core-boundaries/rules/crate-layout.mjs'; @@ -255,6 +260,73 @@ test('service integration tests keep their reviewed explicit target topology', ( assert.deepEqual(checkServicesIntegrationsIntegrationTestTopology(repositoryRoot), []); }); +test('external source integration tests keep reviewed owner and process boundaries', () => { + const repositoryRoot = fileURLToPath(new URL('..', import.meta.url)); + + assert.deepEqual(opencodeAdapterIntegrationTestTargets, [ + { name: 'opencode_mcp_adapter', path: 'tests/opencode_mcp_adapter.rs' }, + { name: 'opencode_source_adapter', path: 'tests/opencode_source_adapter.rs' }, + { + name: 'opencode_static_source_contracts', + path: 'tests/opencode_static_source_contracts.rs', + leaves: [ + 'tests/opencode_static_source_contracts/hook_source.rs', + 'tests/opencode_static_source_contracts/opencode_command_adapter.rs', + 'tests/opencode_static_source_contracts/opencode_skill_roots.rs', + 'tests/opencode_static_source_contracts/opencode_subagent_adapter.rs', + 'tests/opencode_static_source_contracts/opencode_workspace_references.rs', + ], + forbidRequiredFeatures: true, + }, + { name: 'tool_source_contracts', path: 'tests/tool_source_contracts.rs' }, + ]); + assert.deepEqual(claudeCodeAdapterIntegrationTestTargets, [ + { + name: 'claude_code_source_contracts', + path: 'tests/claude_code_source_contracts.rs', + leaves: [ + 'tests/claude_code_source_contracts/command_source.rs', + 'tests/claude_code_source_contracts/hook_source.rs', + 'tests/claude_code_source_contracts/mcp_source.rs', + 'tests/claude_code_source_contracts/subagent_source.rs', + ], + forbidRequiredFeatures: true, + }, + ]); + assert.deepEqual(codexAdapterIntegrationTestTargets, [ + { + name: 'codex_source_contracts', + path: 'tests/codex_source_contracts.rs', + leaves: [ + 'tests/codex_source_contracts/hook_source.rs', + 'tests/codex_source_contracts/mcp_source.rs', + 'tests/codex_source_contracts/subagent_source.rs', + ], + forbidRequiredFeatures: true, + }, + ]); + assert.deepEqual(externalSourcesIntegrationTestTargets, [ + { + name: 'external_source_coordination_contracts', + path: 'tests/external_source_coordination_contracts.rs', + leaves: [ + 'tests/external_source_coordination_contracts/control_plane.rs', + 'tests/external_source_coordination_contracts/coordinator_contracts.rs', + 'tests/external_source_coordination_contracts/hook_coordinator.rs', + 'tests/external_source_coordination_contracts/mcp_coordinator.rs', + 'tests/external_source_coordination_contracts/subagent_coordinator.rs', + 'tests/external_source_coordination_contracts/tool_coordinator_contracts.rs', + 'tests/external_source_coordination_contracts/workspace_reference.rs', + ], + forbidRequiredFeatures: true, + }, + ]); + assert.deepEqual( + checkExternalSourceIntegrationTestTopologies(repositoryRoot), + [], + ); +}); + test('runtime-services test support is absent from ordinary library builds', async () => { const [manifest, library] = await Promise.all([ readFile( @@ -506,14 +578,13 @@ test('explicit product entrypoint bitfun-core feature selections pass', () => { ); }); -const ACP_REVIEWED_CORE_FEATURES = [ +const SDK_HOST_REVIEWED_CORE_FEATURES = [ 'agent-runtime', 'document-read', 'subscription-auth', 'deep-research', 'lsp', 'external-sources', - 'ssh-remote', 'tools-basic', 'tools-git', 'tools-mcp', @@ -525,6 +596,11 @@ const ACP_REVIEWED_CORE_FEATURES = [ 'tools-agent-control', ]; +const ACP_REVIEWED_CORE_FEATURES = [ + ...SDK_HOST_REVIEWED_CORE_FEATURES, + 'ssh-remote', +]; + const CLI_REVIEWED_CORE_FEATURES = [ ...ACP_REVIEWED_CORE_FEATURES, 'remote-connect', @@ -537,6 +613,126 @@ const APP_SERVER_REVIEWED_CORE_FEATURES = [ 'remote-connect', ]; +test('SDK Host Core capability closure keeps every reviewed owner', () => { + const core = packageAt('bitfun-core', 'src/crates/assembly/core/Cargo.toml'); + const sdkHost = packageAt( + 'bitfun-sdk-host-app', + 'src/apps/sdk-host/Cargo.toml', + [pathDependency('src/crates/assembly/core', { + name: 'bitfun-core', + usesDefaultFeatures: false, + features: SDK_HOST_REVIEWED_CORE_FEATURES.filter( + (feature) => feature !== 'external-sources', + ), + })], + ); + + const violations = findProductEntrypointCoreFeatureViolations( + [sdkHost, core], + { root: TEST_ROOT, crateLayoutRules }, + ); + + assert.deepEqual(violations.map((violation) => violation.message), [ + 'bitfun-sdk-host-app Core capability closure must include external-sources', + ]); +}); + +test('SDK Host closure rejects unreviewed capability owners below Core', () => { + const cases = [ + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'remote-connect'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'remote-ssh'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'remote-ssh-concrete'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'function-agents'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'announcement'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'debug-log'], + ['bitfun-services-integrations', 'src/crates/services/services-integrations/Cargo.toml', 'product-full'], + ['bitfun-product-domains', 'src/crates/contracts/product-domains/Cargo.toml', 'function-agents'], + ['bitfun-product-domains', 'src/crates/contracts/product-domains/Cargo.toml', 'product-full'], + ['bitfun-services-core', 'src/crates/services/services-core/Cargo.toml', 'dispatch-workspace'], + ]; + + for (const [ownerName, ownerManifest, forbiddenFeature] of cases) { + const core = packageAt('bitfun-core', 'src/crates/assembly/core/Cargo.toml'); + const owner = { + ...packageAt(ownerName, ownerManifest), + features: { [forbiddenFeature]: [] }, + }; + const bridge = packageAt('bridge', 'src/crates/assembly/bridge/Cargo.toml', [ + pathDependency(ownerManifest.replace('/Cargo.toml', ''), { + name: ownerName, + usesDefaultFeatures: false, + features: [forbiddenFeature], + }), + ]); + const sdkHost = packageAt( + 'bitfun-sdk-host-app', + 'src/apps/sdk-host/Cargo.toml', + [ + pathDependency('src/crates/assembly/core', { + name: 'bitfun-core', + usesDefaultFeatures: false, + features: SDK_HOST_REVIEWED_CORE_FEATURES, + }), + pathDependency('src/crates/assembly/bridge', { name: 'bridge' }), + ], + ); + + const violations = findProductEntrypointCoreFeatureViolations( + [sdkHost, bridge, core, owner], + { root: TEST_ROOT, crateLayoutRules }, + ); + + const forbiddenOwner = `${ownerName}/${forbiddenFeature}`; + assert.equal(violations.length, 1, forbiddenOwner); + assert.match( + violations[0].message, + new RegExp(forbiddenOwner), + ); + } +}); + +test('SDK Host closure inspects lower owners forwarded by reviewed Core features', () => { + const ownerManifest = 'src/crates/services/services-integrations/Cargo.toml'; + const core = { + ...packageAt( + 'bitfun-core', + 'src/crates/assembly/core/Cargo.toml', + [pathDependency('src/crates/services/services-integrations', { + name: 'bitfun-services-integrations', + optional: true, + usesDefaultFeatures: false, + })], + ), + features: { + 'external-sources': ['bitfun-services-integrations/remote-connect'], + }, + }; + const owner = { + ...packageAt('bitfun-services-integrations', ownerManifest), + features: { 'remote-connect': [] }, + }; + const sdkHost = packageAt( + 'bitfun-sdk-host-app', + 'src/apps/sdk-host/Cargo.toml', + [pathDependency('src/crates/assembly/core', { + name: 'bitfun-core', + usesDefaultFeatures: false, + features: SDK_HOST_REVIEWED_CORE_FEATURES, + })], + ); + + const violations = findProductEntrypointCoreFeatureViolations( + [sdkHost, core, owner], + { root: TEST_ROOT, crateLayoutRules }, + ); + + assert.equal(violations.length, 1); + assert.match( + violations[0].message, + /bitfun-services-integrations\/remote-connect/, + ); +}); + test('App Server Core capability closure keeps its production Git owner', () => { const core = packageAt('bitfun-core', 'src/crates/assembly/core/Cargo.toml'); const appServer = packageAt( diff --git a/scripts/core-boundaries/cargo-dependency-boundaries.mjs b/scripts/core-boundaries/cargo-dependency-boundaries.mjs index e4c21b01e4..7da696362a 100644 --- a/scripts/core-boundaries/cargo-dependency-boundaries.mjs +++ b/scripts/core-boundaries/cargo-dependency-boundaries.mjs @@ -809,52 +809,39 @@ export function findProductEntrypointCoreFeatureViolations( packages, { root, crateLayoutRules }, ) { + const coreCompatibilityReviewedFeatures = [ + 'agent-runtime', + 'document-read', + 'subscription-auth', + 'deep-research', + 'lsp', + 'external-sources', + 'tools-basic', + 'tools-git', + 'tools-mcp', + 'tools-browser-web', + 'tools-computer-use', + 'tools-image-analysis', + 'tools-miniapp', + 'tools-canvas', + 'tools-agent-control', + ]; const reviewedCoreFeatureClosures = new Map([ ['bitfun-cli', [ - 'agent-runtime', - 'document-read', - 'subscription-auth', + ...coreCompatibilityReviewedFeatures, 'remote-connect', - 'deep-research', - 'lsp', - 'external-sources', 'plugin-runtime', 'ssh-remote', - 'tools-basic', - 'tools-git', - 'tools-mcp', - 'tools-browser-web', - 'tools-computer-use', - 'tools-image-analysis', - 'tools-miniapp', - 'tools-canvas', - 'tools-agent-control', - ]], - ['bitfun-acp', [ - 'agent-runtime', - 'document-read', - 'subscription-auth', - 'deep-research', - 'lsp', - 'external-sources', - 'ssh-remote', - 'tools-basic', - 'tools-git', - 'tools-mcp', - 'tools-browser-web', - 'tools-computer-use', - 'tools-image-analysis', - 'tools-miniapp', - 'tools-canvas', - 'tools-agent-control', ]], + ['bitfun-acp', [...coreCompatibilityReviewedFeatures, 'ssh-remote']], ['bitfun-app-server', [ 'external-sources', 'git', 'remote-connect', ]], + ['bitfun-sdk-host-app', coreCompatibilityReviewedFeatures], ]); - const acpActiveCoreFeatures = [ + const coreCompatibilityActiveFeatures = [ 'agent-runtime', 'ai-adapter-runtime', 'browser-control', @@ -872,12 +859,10 @@ export function findProductEntrypointCoreFeatureViolations( 'plugin-source', 'process-runtime', 'product-capabilities', - 'remote-workspace', 'review-platform', 'runtime-services', 'scheduled-jobs', 'script-tool-runtime', - 'ssh-remote', 'subscription-auth', 'terminal', 'tool-packs', @@ -895,9 +880,15 @@ export function findProductEntrypointCoreFeatureViolations( 'workspace-runtime', 'workspace-watch', ]; + const acpActiveCoreFeatures = [ + ...coreCompatibilityActiveFeatures, + 'remote-workspace', + 'ssh-remote', + ]; const reviewedActiveCoreFeatureClosures = new Map([ ['bitfun-cli', [...acpActiveCoreFeatures, 'plugin-runtime', 'remote-connect']], ['bitfun-acp', acpActiveCoreFeatures], + ['bitfun-sdk-host-app', coreCompatibilityActiveFeatures], ['bitfun-app-server', [ 'agent-runtime', 'ai-adapter-runtime', @@ -925,6 +916,21 @@ export function findProductEntrypointCoreFeatureViolations( 'workspace-watch', ]], ]); + const reviewedForbiddenDependencyOwnerFeatures = new Map([ + ['bitfun-sdk-host-app', new Map([ + ['bitfun-services-integrations', [ + 'announcement', + 'debug-log', + 'function-agents', + 'product-full', + 'remote-connect', + 'remote-ssh', + 'remote-ssh-concrete', + ]], + ['bitfun-product-domains', ['function-agents', 'product-full']], + ['bitfun-services-core', ['dispatch-workspace']], + ])], + ]); const packageByManifest = new Map( packages.map((pkg) => [normalizedPath(pkg.manifest_path), pkg]), ); @@ -1012,7 +1018,10 @@ export function findProductEntrypointCoreFeatureViolations( ['bitfun-cli', 'CLI'], ['bitfun-acp', 'ACP'], ['bitfun-app-server', 'App Server'], + ['bitfun-sdk-host-app', 'SDK Host'], ]).get(rootName) ?? rootName; + const forbiddenOwnerFeatures = + reviewedForbiddenDependencyOwnerFeatures.get(rootName); const packageStates = new Map(); const pending = []; @@ -1151,6 +1160,30 @@ export function findProductEntrypointCoreFeatureViolations( } continue; } + + const forbiddenOwnerFeature = ( + forbiddenOwnerFeatures?.get(targetPackage.name) ?? [] + ).find((feature) => targetState.featureState.active.has(feature)); + if (forbiddenOwnerFeature) { + const forbiddenOwner = `${targetPackage.name}/${forbiddenOwnerFeature}`; + const reportKey = [ + rootName, + targetDependencyKindContext, + forbiddenOwner, + ].join('|'); + if (!reportedUnexpectedFeatures.has(reportKey)) { + reportedUnexpectedFeatures.add(reportKey); + violations.push({ + path: sourcePackage.manifest_path, + line: 1, + message: `${rootLabel} dependency closure must not enable ${forbiddenOwner}: ${[ + ...packagePath, + forbiddenOwner, + ].join(' -> ')}`, + }); + } + continue; + } } } } diff --git a/scripts/core-boundaries/checker.mjs b/scripts/core-boundaries/checker.mjs index 3700f463fb..2247f1ee51 100644 --- a/scripts/core-boundaries/checker.mjs +++ b/scripts/core-boundaries/checker.mjs @@ -42,6 +42,7 @@ import { agentRuntimeIntegrationTestTargets, checkAgentRuntimeIntegrationTestTopology, checkCliIntegrationTestTopology, + checkExternalSourceIntegrationTestTopologies, checkServiceIntegrationTestTopologies, cliIntegrationTestTargets, validateExplicitIntegrationTestTopology, @@ -1125,7 +1126,7 @@ export function runCoreBoundaryCheck() { failures.push(...checkCargoDependencyBoundariesSafely({ root: ROOT, crateLayoutRules })); failures.push(...checkAgentRuntimeIntegrationTestTopology(ROOT)); failures.push(...checkCliIntegrationTestTopology(ROOT)); - failures.push(...checkServiceIntegrationTestTopologies(ROOT)); + failures.push(...checkExternalSourceIntegrationTestTopologies(ROOT), ...checkServiceIntegrationTestTopologies(ROOT)); failures.push(...checkPeerCommandPolicySync(ROOT)); for (const rule of forbiddenManifestDependencyRules) { diff --git a/scripts/core-boundaries/explicit-test-topology.mjs b/scripts/core-boundaries/explicit-test-topology.mjs index 9a157af1d4..d1ff06f9e6 100644 --- a/scripts/core-boundaries/explicit-test-topology.mjs +++ b/scripts/core-boundaries/explicit-test-topology.mjs @@ -46,6 +46,115 @@ export const servicesIntegrationsIntegrationTestTargets = [ { name: 'workspace_search_contracts', path: 'tests/workspace_search_contracts.rs' }, ]; +export const opencodeAdapterIntegrationTestTargets = [ + { name: 'opencode_mcp_adapter', path: 'tests/opencode_mcp_adapter.rs' }, + { name: 'opencode_source_adapter', path: 'tests/opencode_source_adapter.rs' }, + { + name: 'opencode_static_source_contracts', + path: 'tests/opencode_static_source_contracts.rs', + leaves: [ + 'tests/opencode_static_source_contracts/hook_source.rs', + 'tests/opencode_static_source_contracts/opencode_command_adapter.rs', + 'tests/opencode_static_source_contracts/opencode_skill_roots.rs', + 'tests/opencode_static_source_contracts/opencode_subagent_adapter.rs', + 'tests/opencode_static_source_contracts/opencode_workspace_references.rs', + ], + forbidRequiredFeatures: true, + }, + { name: 'tool_source_contracts', path: 'tests/tool_source_contracts.rs' }, +]; + +export const claudeCodeAdapterIntegrationTestTargets = [ + { + name: 'claude_code_source_contracts', + path: 'tests/claude_code_source_contracts.rs', + leaves: [ + 'tests/claude_code_source_contracts/command_source.rs', + 'tests/claude_code_source_contracts/hook_source.rs', + 'tests/claude_code_source_contracts/mcp_source.rs', + 'tests/claude_code_source_contracts/subagent_source.rs', + ], + forbidRequiredFeatures: true, + }, +]; + +export const codexAdapterIntegrationTestTargets = [ + { + name: 'codex_source_contracts', + path: 'tests/codex_source_contracts.rs', + leaves: [ + 'tests/codex_source_contracts/hook_source.rs', + 'tests/codex_source_contracts/mcp_source.rs', + 'tests/codex_source_contracts/subagent_source.rs', + ], + forbidRequiredFeatures: true, + }, +]; + +export const externalSourcesIntegrationTestTargets = [ + { + name: 'external_source_coordination_contracts', + path: 'tests/external_source_coordination_contracts.rs', + leaves: [ + 'tests/external_source_coordination_contracts/control_plane.rs', + 'tests/external_source_coordination_contracts/coordinator_contracts.rs', + 'tests/external_source_coordination_contracts/hook_coordinator.rs', + 'tests/external_source_coordination_contracts/mcp_coordinator.rs', + 'tests/external_source_coordination_contracts/subagent_coordinator.rs', + 'tests/external_source_coordination_contracts/tool_coordinator_contracts.rs', + 'tests/external_source_coordination_contracts/workspace_reference.rs', + ], + forbidRequiredFeatures: true, + }, +]; + +function decodeBasicTomlKey(token) { + let decoded = ''; + const simpleEscapes = new Map([ + ['b', '\b'], ['t', '\t'], ['n', '\n'], ['f', '\f'], ['r', '\r'], + ['"', '"'], ['\\', '\\'], + ]); + for (let index = 1; index < token.length - 1; index += 1) { + if (token[index] !== '\\') { + decoded += token[index]; + continue; + } + index += 1; + const escape = token[index]; + if (simpleEscapes.has(escape)) { + decoded += simpleEscapes.get(escape); + continue; + } + if (escape !== 'u' && escape !== 'U') { + return null; + } + const digitCount = escape === 'u' ? 4 : 8; + const hex = token.slice(index + 1, index + 1 + digitCount); + if (!new RegExp(`^[0-9a-fA-F]{${digitCount}}$`).test(hex)) { + return null; + } + const codePoint = Number.parseInt(hex, 16); + if (codePoint > 0x10FFFF || (codePoint >= 0xD800 && codePoint <= 0xDFFF)) { + return null; + } + decoded += String.fromCodePoint(codePoint); + index += digitCount; + } + return decoded; +} + +function tomlFieldName(line) { + const match = line.match(/^([A-Za-z0-9_-]+|'[^']*'|"(?:[^"\\]|\\.)*")\s*=/); + if (!match) { + return null; + } + const token = match[1]; + if (token.startsWith("'")) { + return token.slice(1, -1); + } + return token.startsWith('"') ? decodeBasicTomlKey(token) : token; +} + function parseExplicitTestTargets(manifestText) { const targets = []; let current = null; @@ -67,6 +176,9 @@ function parseExplicitTestTargets(manifestText) { finishCurrent(); continue; } + if (current && tomlFieldName(trimmed) === 'required-features') { + current.hasRequiredFeatures = true; + } const field = current && trimmed.match(/^(name|path)\s*=\s*"([^"]+)"\s*$/); if (field) { current[field[1]] = field[2]; @@ -386,12 +498,23 @@ export function validateExplicitIntegrationTestTopology({ } const expectedTargetEntries = expectedTargets.map(({ name, path }) => `${name}=${path}`).sort(); - const actualTargetEntries = parseExplicitTestTargets(manifestText) + const actualTargets = parseExplicitTestTargets(manifestText); + const actualTargetEntries = actualTargets .map(({ name, path }) => `${name ?? ''}=${path ?? ''}`) .sort(); if (actualTargetEntries.join('\n') !== expectedTargetEntries.join('\n')) { errors.push(`explicit test targets must be exactly: ${expectedTargetEntries.join(', ')}`); } + const targetsWithoutRequiredFeatures = new Set( + expectedTargets + .filter(({ forbidRequiredFeatures }) => forbidRequiredFeatures) + .map(({ name, path }) => `${name}=${path}`), + ); + for (const { name, path, hasRequiredFeatures } of actualTargets) { + if (hasRequiredFeatures && targetsWithoutRequiredFeatures.has(`${name}=${path}`)) { + errors.push(`explicit test target ${name} must not declare required-features`); + } + } const expectedRoots = expectedTargets.map(({ path }) => path).sort(); if ([...topLevelRustFiles].sort().join('\n') !== expectedRoots.join('\n')) { @@ -399,6 +522,13 @@ export function validateExplicitIntegrationTestTopology({ } const leaves = new Set(leafRustFiles); + const expectedLeaves = expectedTargets.flatMap(({ leaves: targetLeaves = [] }) => targetLeaves).sort(); + if ( + expectedLeaves.length > 0 + && [...leaves].sort().join('\n') !== expectedLeaves.join('\n') + ) { + errors.push(`grouped test leaves must be exactly: ${expectedLeaves.join(', ')}`); + } const referenceCounts = new Map(); for (const root of expectedRoots) { const source = rootSources.get(root); @@ -543,6 +673,44 @@ export function checkServicesIntegrationsIntegrationTestTopology(root) { }); } +export function checkOpencodeAdapterIntegrationTestTopology(root) { + return checkExplicitIntegrationTestTopology(root, { + cratePath: 'src/crates/adapters/opencode-adapter', + expectedTargets: opencodeAdapterIntegrationTestTargets, + ignoredDirectories: ['tests/fixtures'], + }); +} + +export function checkClaudeCodeAdapterIntegrationTestTopology(root) { + return checkExplicitIntegrationTestTopology(root, { + cratePath: 'src/crates/adapters/claude-code-adapter', + expectedTargets: claudeCodeAdapterIntegrationTestTargets, + }); +} + +export function checkCodexAdapterIntegrationTestTopology(root) { + return checkExplicitIntegrationTestTopology(root, { + cratePath: 'src/crates/adapters/codex-adapter', + expectedTargets: codexAdapterIntegrationTestTargets, + }); +} + +export function checkExternalSourcesIntegrationTestTopology(root) { + return checkExplicitIntegrationTestTopology(root, { + cratePath: 'src/crates/assembly/external-sources', + expectedTargets: externalSourcesIntegrationTestTargets, + }); +} + +export function checkExternalSourceIntegrationTestTopologies(root) { + return [ + ...checkOpencodeAdapterIntegrationTestTopology(root), + ...checkClaudeCodeAdapterIntegrationTestTopology(root), + ...checkCodexAdapterIntegrationTestTopology(root), + ...checkExternalSourcesIntegrationTestTopology(root), + ]; +} + export function checkServiceIntegrationTestTopologies(root) { return [ ...checkServicesCoreIntegrationTestTopology(root), diff --git a/scripts/core-boundaries/rules/source/forbidden-rules.mjs b/scripts/core-boundaries/rules/source/forbidden-rules.mjs index d6eaab22e7..43576f863e 100644 --- a/scripts/core-boundaries/rules/source/forbidden-rules.mjs +++ b/scripts/core-boundaries/rules/source/forbidden-rules.mjs @@ -4135,13 +4135,13 @@ export const forbiddenContentUnderRules = [ /\b(?:use\s+bitfun_opencode_adapter\b|extern\s+crate\s+bitfun_opencode_adapter\b|bitfun_opencode_adapter::)/, allowPaths: [ 'src/crates/adapters/opencode-adapter/tests/opencode_source_adapter.rs', - 'src/crates/adapters/opencode-adapter/tests/opencode_command_adapter.rs', - 'src/crates/adapters/opencode-adapter/tests/opencode_skill_roots.rs', - 'src/crates/adapters/opencode-adapter/tests/opencode_workspace_references.rs', + 'src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_command_adapter.rs', + 'src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_skill_roots.rs', + 'src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_workspace_references.rs', 'src/crates/adapters/opencode-adapter/tests/tool_source_contracts.rs', - 'src/crates/adapters/opencode-adapter/tests/opencode_subagent_adapter.rs', + 'src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_subagent_adapter.rs', 'src/crates/adapters/opencode-adapter/tests/opencode_mcp_adapter.rs', - 'src/crates/adapters/opencode-adapter/tests/hook_source.rs', + 'src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/hook_source.rs', 'src/crates/assembly/core/src/plugin_runtime.rs', 'src/crates/assembly/core/src/external_sources.rs', 'src/crates/assembly/core/src/external_hooks.rs', @@ -4171,10 +4171,10 @@ export const forbiddenContentUnderRules = [ patterns: [{ regex: /\b(?:use\s+bitfun_claude_code_adapter\b|extern\s+crate\s+bitfun_claude_code_adapter\b|bitfun_claude_code_adapter::)/, allowPaths: [ - 'src/crates/adapters/claude-code-adapter/tests/hook_source.rs', - 'src/crates/adapters/claude-code-adapter/tests/command_source.rs', - 'src/crates/adapters/claude-code-adapter/tests/subagent_source.rs', - 'src/crates/adapters/claude-code-adapter/tests/mcp_source.rs', + 'src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/hook_source.rs', + 'src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/command_source.rs', + 'src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/subagent_source.rs', + 'src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/mcp_source.rs', 'src/crates/assembly/core/src/external_sources.rs', 'src/crates/assembly/core/src/external_hooks.rs', 'src/crates/assembly/core/src/instruction_sources.rs', @@ -4188,9 +4188,9 @@ export const forbiddenContentUnderRules = [ patterns: [{ regex: /\b(?:use\s+bitfun_codex_adapter\b|extern\s+crate\s+bitfun_codex_adapter\b|bitfun_codex_adapter::)/, allowPaths: [ - 'src/crates/adapters/codex-adapter/tests/hook_source.rs', - 'src/crates/adapters/codex-adapter/tests/subagent_source.rs', - 'src/crates/adapters/codex-adapter/tests/mcp_source.rs', + 'src/crates/adapters/codex-adapter/tests/codex_source_contracts/hook_source.rs', + 'src/crates/adapters/codex-adapter/tests/codex_source_contracts/subagent_source.rs', + 'src/crates/adapters/codex-adapter/tests/codex_source_contracts/mcp_source.rs', 'src/crates/assembly/core/src/external_sources.rs', 'src/crates/assembly/core/src/external_hooks.rs', 'src/crates/assembly/core/src/instruction_sources.rs', diff --git a/scripts/core-boundaries/self-test.mjs b/scripts/core-boundaries/self-test.mjs index 2f4e8d9b34..666ce0d0ed 100644 --- a/scripts/core-boundaries/self-test.mjs +++ b/scripts/core-boundaries/self-test.mjs @@ -152,6 +152,53 @@ export function runManifestParserSelfTest({ if (!orphanErrors.some((error) => error.includes('orphan_contracts.rs'))) { throw new Error('explicit integration-test topology must reject an orphan leaf test'); } + const reviewedLeafTargets = agentRuntimeIntegrationTestTargets.map((target) => ( + target.path === 'tests/agent_definition_contracts.rs' + ? { + ...target, + leaves: ['tests/agent_definition_contracts/prompt_contracts.rs'], + forbidRequiredFeatures: true, + } + : target + )); + const missingReviewedLeafErrors = validateExplicitIntegrationTestTopology({ + ...explicitTestFixture, + expectedTargets: reviewedLeafTargets, + leafRustFiles: [], + leafSources: new Map(), + }); + if (!missingReviewedLeafErrors.some((error) => error.includes('grouped test leaves'))) { + throw new Error('explicit integration-test topology must reject a removed reviewed leaf'); + } + for (const requiredFeaturesDeclaration of [ + 'required-features = [\n "opt-in",\n]', + '"required\\u002dfeatures" = ["opt-in"]', + ]) { + const unexpectedRequiredFeaturesErrors = validateExplicitIntegrationTestTopology({ + ...explicitTestFixture, + expectedTargets: reviewedLeafTargets, + manifestText: explicitTestManifest.replace( + 'path = "tests/agent_definition_contracts.rs"', + `path = "tests/agent_definition_contracts.rs"\n${requiredFeaturesDeclaration}`, + ), + }); + if (!unexpectedRequiredFeaturesErrors.some((error) => error.includes('required-features'))) { + throw new Error(`ungated explicit test topology accepted: ${requiredFeaturesDeclaration}`); + } + } + const independentRequiredFeaturesErrors = validateExplicitIntegrationTestTopology({ + ...explicitTestFixture, + expectedTargets: reviewedLeafTargets, + manifestText: explicitTestManifest.replace( + 'path = "tests/native_hook_execution_contracts.rs"', + 'path = "tests/native_hook_execution_contracts.rs"\nrequired-features = ["native-hooks"]', + ), + }); + if (independentRequiredFeaturesErrors.length > 0) { + throw new Error( + `target-scoped required-features contract rejected an independent target: ${independentRequiredFeaturesErrors.join('; ')}`, + ); + } const reviewedLeafCfgFixture = { ...explicitTestFixture, leafSources: new Map([[ diff --git a/src/apps/sdk-host/Cargo.toml b/src/apps/sdk-host/Cargo.toml index bf80751689..834d86cd79 100644 --- a/src/apps/sdk-host/Cargo.toml +++ b/src/apps/sdk-host/Cargo.toml @@ -14,9 +14,26 @@ path = "src/main.rs" anyhow = { workspace = true } async-trait = { workspace = true } bitfun-agent-runtime = { path = "../../crates/execution/agent-runtime" } -bitfun-core = { path = "../../crates/assembly/core", default-features = false, features = ["product-full"] } +bitfun-core = { path = "../../crates/assembly/core", default-features = false, features = [ + "agent-runtime", + "document-read", + "subscription-auth", + "deep-research", + "lsp", + "external-sources", + "tools-basic", + "tools-git", + "tools-mcp", + "tools-browser-web", + "tools-computer-use", + "tools-image-analysis", + "tools-miniapp", + "tools-canvas", + "tools-agent-control", +] } bitfun-sdk-host = { path = "../../crates/interfaces/sdk-host" } futures-util = { workspace = true } +rustls = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true, features = ["io-std", "io-util", "macros", "rt-multi-thread", "sync", "time"] } tokio-util = { workspace = true, features = ["codec"] } @@ -24,7 +41,6 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } [dev-dependencies] -rustls = { workspace = true } tempfile = "3" tokio = { workspace = true, features = ["process"] } diff --git a/src/apps/sdk-host/src/lib.rs b/src/apps/sdk-host/src/lib.rs index b8a32c20fa..96df713457 100644 --- a/src/apps/sdk-host/src/lib.rs +++ b/src/apps/sdk-host/src/lib.rs @@ -4,13 +4,13 @@ pub mod transport; /// Stack size used by the SDK Host worker. /// -/// The Host initializes the same full Agent Runtime as the CLI and preserves -/// the reviewed Windows stack-overflow protection used by that runtime. +/// The Host initializes its reviewed SDK capability profile and preserves the +/// Windows stack-overflow protection used by the shared Agent Runtime. pub const SDK_HOST_WORKER_STACK_BYTES: usize = 16 * 1024 * 1024; /// Installs process-global prerequisites before any TLS-capable service starts. pub fn initialize_process_runtime() { - bitfun_core::service::remote_connect::ensure_rustls_crypto_provider(); + let _ = rustls::crypto::ring::default_provider().install_default(); } /// Spawns the SDK Host runtime on the reviewed worker-stack boundary. diff --git a/src/apps/sdk-host/src/runtime.rs b/src/apps/sdk-host/src/runtime.rs index f9da5654ac..525c1eed07 100644 --- a/src/apps/sdk-host/src/runtime.rs +++ b/src/apps/sdk-host/src/runtime.rs @@ -87,9 +87,6 @@ fn bind_core_execution_ports(agentic_system: &AgenticSystem) { agentic_system .coordinator .set_terminal_port(CoreRuntimeServicesProvider::terminal_port()); - agentic_system - .coordinator - .set_remote_exec_port(CoreRuntimeServicesProvider::remote_exec_port()); } pub(crate) async fn initialize_terminal_service() { diff --git a/src/crates/adapters/claude-code-adapter/Cargo.toml b/src/crates/adapters/claude-code-adapter/Cargo.toml index dc7ae1510e..4cceb1484a 100644 --- a/src/crates/adapters/claude-code-adapter/Cargo.toml +++ b/src/crates/adapters/claude-code-adapter/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true authors.workspace = true edition.workspace = true description = "Claude Code static source adapter for BitFun" +autotests = false [lib] name = "bitfun_claude_code_adapter" @@ -26,5 +27,9 @@ url = { workspace = true } [dev-dependencies] tempfile = { workspace = true } +[[test]] +name = "claude_code_source_contracts" +path = "tests/claude_code_source_contracts.rs" + [lints] workspace = true diff --git a/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts.rs b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts.rs new file mode 100644 index 0000000000..29346cbf23 --- /dev/null +++ b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts.rs @@ -0,0 +1,8 @@ +#[path = "claude_code_source_contracts/command_source.rs"] +mod command_source; +#[path = "claude_code_source_contracts/hook_source.rs"] +mod hook_source; +#[path = "claude_code_source_contracts/mcp_source.rs"] +mod mcp_source; +#[path = "claude_code_source_contracts/subagent_source.rs"] +mod subagent_source; diff --git a/src/crates/adapters/claude-code-adapter/tests/command_source.rs b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/command_source.rs similarity index 100% rename from src/crates/adapters/claude-code-adapter/tests/command_source.rs rename to src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/command_source.rs diff --git a/src/crates/adapters/claude-code-adapter/tests/hook_source.rs b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/hook_source.rs similarity index 100% rename from src/crates/adapters/claude-code-adapter/tests/hook_source.rs rename to src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/hook_source.rs diff --git a/src/crates/adapters/claude-code-adapter/tests/mcp_source.rs b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/mcp_source.rs similarity index 100% rename from src/crates/adapters/claude-code-adapter/tests/mcp_source.rs rename to src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/mcp_source.rs diff --git a/src/crates/adapters/claude-code-adapter/tests/subagent_source.rs b/src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/subagent_source.rs similarity index 100% rename from src/crates/adapters/claude-code-adapter/tests/subagent_source.rs rename to src/crates/adapters/claude-code-adapter/tests/claude_code_source_contracts/subagent_source.rs diff --git a/src/crates/adapters/codex-adapter/Cargo.toml b/src/crates/adapters/codex-adapter/Cargo.toml index 9d6faf176f..92b9350337 100644 --- a/src/crates/adapters/codex-adapter/Cargo.toml +++ b/src/crates/adapters/codex-adapter/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true authors.workspace = true edition.workspace = true description = "Codex static source adapter for BitFun" +autotests = false [lib] name = "bitfun_codex_adapter" @@ -24,5 +25,9 @@ url = { workspace = true } [dev-dependencies] tempfile = { workspace = true } +[[test]] +name = "codex_source_contracts" +path = "tests/codex_source_contracts.rs" + [lints] workspace = true diff --git a/src/crates/adapters/codex-adapter/tests/codex_source_contracts.rs b/src/crates/adapters/codex-adapter/tests/codex_source_contracts.rs new file mode 100644 index 0000000000..6c033129c2 --- /dev/null +++ b/src/crates/adapters/codex-adapter/tests/codex_source_contracts.rs @@ -0,0 +1,6 @@ +#[path = "codex_source_contracts/hook_source.rs"] +mod hook_source; +#[path = "codex_source_contracts/mcp_source.rs"] +mod mcp_source; +#[path = "codex_source_contracts/subagent_source.rs"] +mod subagent_source; diff --git a/src/crates/adapters/codex-adapter/tests/hook_source.rs b/src/crates/adapters/codex-adapter/tests/codex_source_contracts/hook_source.rs similarity index 100% rename from src/crates/adapters/codex-adapter/tests/hook_source.rs rename to src/crates/adapters/codex-adapter/tests/codex_source_contracts/hook_source.rs diff --git a/src/crates/adapters/codex-adapter/tests/mcp_source.rs b/src/crates/adapters/codex-adapter/tests/codex_source_contracts/mcp_source.rs similarity index 100% rename from src/crates/adapters/codex-adapter/tests/mcp_source.rs rename to src/crates/adapters/codex-adapter/tests/codex_source_contracts/mcp_source.rs diff --git a/src/crates/adapters/codex-adapter/tests/subagent_source.rs b/src/crates/adapters/codex-adapter/tests/codex_source_contracts/subagent_source.rs similarity index 100% rename from src/crates/adapters/codex-adapter/tests/subagent_source.rs rename to src/crates/adapters/codex-adapter/tests/codex_source_contracts/subagent_source.rs diff --git a/src/crates/adapters/opencode-adapter/AGENTS-CN.md b/src/crates/adapters/opencode-adapter/AGENTS-CN.md index a2aa76cb38..585d7769da 100644 --- a/src/crates/adapters/opencode-adapter/AGENTS-CN.md +++ b/src/crates/adapters/opencode-adapter/AGENTS-CN.md @@ -54,9 +54,10 @@ ## 验证 - `cargo test -p bitfun-opencode-adapter --test opencode_source_adapter` -- `cargo test -p bitfun-opencode-adapter --test opencode_command_adapter` +- `cargo test -p bitfun-opencode-adapter --test opencode_mcp_adapter` +- `cargo test -p bitfun-opencode-adapter --test opencode_static_source_contracts opencode_command_adapter::` +- `cargo test -p bitfun-opencode-adapter --test opencode_static_source_contracts opencode_subagent_adapter::` - `cargo test -p bitfun-opencode-adapter --test tool_source_contracts` -- `cargo test -p bitfun-opencode-adapter --test opencode_subagent_adapter` -- `cargo test -p bitfun-opencode-adapter p0_c2_fixture` -- `cargo test -p bitfun-opencode-adapter client_path_projects_trusted_custom_tool_candidate_with_permission_prompt` +- `cargo test -p bitfun-opencode-adapter --lib p0_c2_fixture` +- `cargo test -p bitfun-opencode-adapter --lib client_path_projects_trusted_custom_tool_candidate_with_permission_prompt` - `node scripts/check-core-boundaries.mjs` diff --git a/src/crates/adapters/opencode-adapter/AGENTS.md b/src/crates/adapters/opencode-adapter/AGENTS.md index 550c004e7e..1750584bb9 100644 --- a/src/crates/adapters/opencode-adapter/AGENTS.md +++ b/src/crates/adapters/opencode-adapter/AGENTS.md @@ -114,9 +114,10 @@ Product-source boundary: ## Verification - `cargo test -p bitfun-opencode-adapter --test opencode_source_adapter` -- `cargo test -p bitfun-opencode-adapter --test opencode_command_adapter` +- `cargo test -p bitfun-opencode-adapter --test opencode_mcp_adapter` +- `cargo test -p bitfun-opencode-adapter --test opencode_static_source_contracts opencode_command_adapter::` +- `cargo test -p bitfun-opencode-adapter --test opencode_static_source_contracts opencode_subagent_adapter::` - `cargo test -p bitfun-opencode-adapter --test tool_source_contracts` -- `cargo test -p bitfun-opencode-adapter --test opencode_subagent_adapter` -- `cargo test -p bitfun-opencode-adapter p0_c2_fixture` -- `cargo test -p bitfun-opencode-adapter client_path_projects_trusted_custom_tool_candidate_with_permission_prompt` +- `cargo test -p bitfun-opencode-adapter --lib p0_c2_fixture` +- `cargo test -p bitfun-opencode-adapter --lib client_path_projects_trusted_custom_tool_candidate_with_permission_prompt` - `node scripts/check-core-boundaries.mjs` diff --git a/src/crates/adapters/opencode-adapter/Cargo.toml b/src/crates/adapters/opencode-adapter/Cargo.toml index 804fd1e03e..9ae15880a8 100644 --- a/src/crates/adapters/opencode-adapter/Cargo.toml +++ b/src/crates/adapters/opencode-adapter/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true authors.workspace = true edition.workspace = true description = "OpenCode-compatible source and candidate adapter for BitFun" +autotests = false [lib] name = "bitfun_opencode_adapter" @@ -35,5 +36,21 @@ bitfun-services-integrations = { path = "../../services/services-integrations", tokio = { workspace = true, features = ["macros", "rt"] } tempfile = { workspace = true } +[[test]] +name = "opencode_mcp_adapter" +path = "tests/opencode_mcp_adapter.rs" + +[[test]] +name = "opencode_source_adapter" +path = "tests/opencode_source_adapter.rs" + +[[test]] +name = "opencode_static_source_contracts" +path = "tests/opencode_static_source_contracts.rs" + +[[test]] +name = "tool_source_contracts" +path = "tests/tool_source_contracts.rs" + [lints] workspace = true diff --git a/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts.rs new file mode 100644 index 0000000000..112f66dc2c --- /dev/null +++ b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts.rs @@ -0,0 +1,10 @@ +#[path = "opencode_static_source_contracts/hook_source.rs"] +mod hook_source; +#[path = "opencode_static_source_contracts/opencode_command_adapter.rs"] +mod opencode_command_adapter; +#[path = "opencode_static_source_contracts/opencode_skill_roots.rs"] +mod opencode_skill_roots; +#[path = "opencode_static_source_contracts/opencode_subagent_adapter.rs"] +mod opencode_subagent_adapter; +#[path = "opencode_static_source_contracts/opencode_workspace_references.rs"] +mod opencode_workspace_references; diff --git a/src/crates/adapters/opencode-adapter/tests/hook_source.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/hook_source.rs similarity index 100% rename from src/crates/adapters/opencode-adapter/tests/hook_source.rs rename to src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/hook_source.rs diff --git a/src/crates/adapters/opencode-adapter/tests/opencode_command_adapter.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_command_adapter.rs similarity index 100% rename from src/crates/adapters/opencode-adapter/tests/opencode_command_adapter.rs rename to src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_command_adapter.rs diff --git a/src/crates/adapters/opencode-adapter/tests/opencode_skill_roots.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_skill_roots.rs similarity index 100% rename from src/crates/adapters/opencode-adapter/tests/opencode_skill_roots.rs rename to src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_skill_roots.rs diff --git a/src/crates/adapters/opencode-adapter/tests/opencode_subagent_adapter.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_subagent_adapter.rs similarity index 100% rename from src/crates/adapters/opencode-adapter/tests/opencode_subagent_adapter.rs rename to src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_subagent_adapter.rs diff --git a/src/crates/adapters/opencode-adapter/tests/opencode_workspace_references.rs b/src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_workspace_references.rs similarity index 100% rename from src/crates/adapters/opencode-adapter/tests/opencode_workspace_references.rs rename to src/crates/adapters/opencode-adapter/tests/opencode_static_source_contracts/opencode_workspace_references.rs diff --git a/src/crates/assembly/external-sources/Cargo.toml b/src/crates/assembly/external-sources/Cargo.toml index 3dcdc619c8..de2dcbaa97 100644 --- a/src/crates/assembly/external-sources/Cargo.toml +++ b/src/crates/assembly/external-sources/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true authors.workspace = true edition.workspace = true description = "Ecosystem-neutral external source lifecycle coordination" +autotests = false [lib] name = "bitfun_external_sources" @@ -18,5 +19,9 @@ tokio = { workspace = true, features = ["rt", "sync", "time"] } [dev-dependencies] tokio = { workspace = true, features = ["macros"] } +[[test]] +name = "external_source_coordination_contracts" +path = "tests/external_source_coordination_contracts.rs" + [lints] workspace = true diff --git a/src/crates/assembly/external-sources/tests/external_source_coordination_contracts.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts.rs new file mode 100644 index 0000000000..28c08e6eb0 --- /dev/null +++ b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts.rs @@ -0,0 +1,14 @@ +#[path = "external_source_coordination_contracts/control_plane.rs"] +mod control_plane; +#[path = "external_source_coordination_contracts/coordinator_contracts.rs"] +mod coordinator_contracts; +#[path = "external_source_coordination_contracts/hook_coordinator.rs"] +mod hook_coordinator; +#[path = "external_source_coordination_contracts/mcp_coordinator.rs"] +mod mcp_coordinator; +#[path = "external_source_coordination_contracts/subagent_coordinator.rs"] +mod subagent_coordinator; +#[path = "external_source_coordination_contracts/tool_coordinator_contracts.rs"] +mod tool_coordinator_contracts; +#[path = "external_source_coordination_contracts/workspace_reference.rs"] +mod workspace_reference; diff --git a/src/crates/assembly/external-sources/tests/control_plane.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/control_plane.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/control_plane.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/control_plane.rs diff --git a/src/crates/assembly/external-sources/tests/coordinator_contracts.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/coordinator_contracts.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/coordinator_contracts.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/coordinator_contracts.rs diff --git a/src/crates/assembly/external-sources/tests/hook_coordinator.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/hook_coordinator.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/hook_coordinator.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/hook_coordinator.rs diff --git a/src/crates/assembly/external-sources/tests/mcp_coordinator.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/mcp_coordinator.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/mcp_coordinator.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/mcp_coordinator.rs diff --git a/src/crates/assembly/external-sources/tests/subagent_coordinator.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/subagent_coordinator.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/subagent_coordinator.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/subagent_coordinator.rs diff --git a/src/crates/assembly/external-sources/tests/tool_coordinator_contracts.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/tool_coordinator_contracts.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/tool_coordinator_contracts.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/tool_coordinator_contracts.rs diff --git a/src/crates/assembly/external-sources/tests/workspace_reference.rs b/src/crates/assembly/external-sources/tests/external_source_coordination_contracts/workspace_reference.rs similarity index 100% rename from src/crates/assembly/external-sources/tests/workspace_reference.rs rename to src/crates/assembly/external-sources/tests/external_source_coordination_contracts/workspace_reference.rs diff --git a/src/crates/assembly/product-capabilities/src/lib.rs b/src/crates/assembly/product-capabilities/src/lib.rs index cda9fc2901..170979f33f 100644 --- a/src/crates/assembly/product-capabilities/src/lib.rs +++ b/src/crates/assembly/product-capabilities/src/lib.rs @@ -276,7 +276,7 @@ const PRODUCT_DELIVERY_PROFILE_ENTRIES: &[ProductDeliveryProfileEntry] = &[ ), ProductDeliveryProfileEntry::new( DeliveryProfile::Sdk, - ProductCoreDependencyMode::ProductFullCompatibility, + ProductCoreDependencyMode::ExplicitCoreCapabilityClosure, ), ]; diff --git a/src/crates/assembly/product-capabilities/tests/product_capabilities.rs b/src/crates/assembly/product-capabilities/tests/product_capabilities.rs index 3e3dc7d613..0e136bce6b 100644 --- a/src/crates/assembly/product-capabilities/tests/product_capabilities.rs +++ b/src/crates/assembly/product-capabilities/tests/product_capabilities.rs @@ -410,7 +410,7 @@ fn product_delivery_profile_matrix_documents_current_core_dependency_shape() { ), ( DeliveryProfile::Sdk, - ProductCoreDependencyMode::ProductFullCompatibility, + ProductCoreDependencyMode::ExplicitCoreCapabilityClosure, ), ] );