游乐游手机版
首页/AI教程/文章详情

深入解析Claude Code泄露代码中的Harness工程究竟是什么样

时间:2026-06-01 11:39
Harness 工程:设计、实现与可借鉴点 这篇文章主要探讨仓库里 rust 目录下那个兼容性测试工具(harness)的目标、结构及具体实现方式。同时,也会提炼出一些对大型代码库迁移、双语言共存这类项目有参考价值的思路。需要先说明的是,主程序真正的源代码仍位于 src (TypeScrip

Harness 工程:设计、实现与可借鉴点

这篇文章主要探讨仓库里 rust/ 目录下那个兼容性测试工具(harness)的目标、结构及具体实现方式。同时,也会提炼出一些对大型代码库迁移、双语言共存这类项目有参考价值的思路。需要先说明的是,主程序真正的源代码仍位于 ../src/(TypeScript)侧;Rust 端当前的目标非常明确——实现“可证明的抽取”以及“与上游接口对齐的骨架”,而非构建一个完整的命令行运行时。

1. 先说说“Harness-first”这个理念怎么落地

rust/README.md 里引用了一份产品需求文档,其核心思路可以概括为四个步骤:

  1. 先抽取“可观测的事实”:从现有上游源码中稳定地读取命令表、工具表、启动阶段这些信息,而不是一上来就闷头编写一套自以为与上游对齐的运行时。
  2. 边界命名与上游保持一致:crate 的划分要紧贴主仓库已有的接口,比如 commandstools、runtime 等。这样两边对照查看、后续往里填充内容都非常方便。
  3. 先证明,再扩张:通过测试和一个小型命令行子命令,将抽取结果固定在持续集成里。新功能必须先在这个测试层中拿出证据,证明走通之后,再考虑行为兼容问题。
  4. 公开承认缺口:在里程碑描述中明确说明现阶段不做“完全替代”的全量兼容。这能有效避免对外界做出过度承诺。

本质上,这是一种“兼容性优先的脚手架”策略,其核心价值在于降低迁移过程中的不确定性与回归成本,而不是第一天就把主程序换掉。

2. 工作空间的依赖关系

来看一下目录结构:

rust/
├── Cargo.toml          # 工作区,members = crates/*,统一 lint 规则
├── README.md
└── crates/
    ├── rusty-claude-cli/    # 二进制入口(很薄的一层)
    ├── compat-harness/      # 读取 TypeScript 源、做抽取的唯一实现 crate
    ├── runtime/             # BootstrapPhase / BootstrapPlan(没有 I/O)
    ├── commands/            # CommandRegistry 等纯数据类型 crate
    └── tools/               # ToolRegistry 等纯数据类型 crate

依赖关系清晰,构成了一个有向无环图。其中,commandstools 之间没有依赖关系——清单彼此独立,避免类型相互拖累。compat-harness 负责聚合解析逻辑和文件读取(I/O),而其他 crate 则保持为可单独测试、可复用的小型库。

3. 上游路径契约:UpstreamPaths

compat-harness 假定仓库的布局是固定的,与当前的单一仓库保持一致:

方法指向
commands_path()repo_root/src/commands.ts
tools_path()repo_root/src/tools.ts
cli_path()repo_root/src/entrypoints/cli.tsx

from_workspace_dir 这个方法通过 canonicalizeparent(),能从 rust/crates/compat-harness/../../ 推导出 repo_root 的位置。这里有一个设计值得借鉴:把“真源文件的位置”封装成一个类型,后续若要改路径,只需改这一个地方即可。测试时则用 CARGO_MANIFEST_DIR 来定位测试用的固定数据文件。

4. 抽取是怎么做的?(启发式逐行扫描,不是 TypeScript 的抽象语法树)

4.1 设计上的取舍

并没有一上来就用 swc 或 TypeScript 的解析器,而是针对那些注册表类型的文件(里面大量充斥着 importexport const … = [feature() ? require 这类模式)采用了逐行扫描并搭配简单字符串规则的方式。

优点缺点
不依赖第三方解析库、实现代码短、编译速度快上游如果大幅度改格式,可能会误报或漏报
与“清单级别”的目标匹配,不做语义分析无法替代类型检查或真实的 import 依赖图

用在这个第一阶段刚刚好。后续如果需求细化到要精确分析导出图或循环依赖,可以再增量式地接入抽象语法树或 ts-morph,而且不需要推翻已有的 crate 划分。

4.2 命令清单(extract_commands

  • 内置命令:匹配 import … from './commands/...' 模式,导入的符号记录为 CommandSource::Builtin
  • 内部列表:在 INTERNAL_ONLY_COMMANDS = [ 和对应的闭合 ] 之间,按行提取标识符,标记为 InternalOnly
  • 特性门控命令:如果一行中同时出现 feature('./commands/,就取赋值操作左侧的变量名,标记为 FeatureGated
  • 去重:最终以 (name, source) 这个二元组为键进行去重,形成 CommandRegistry

4.3 工具清单(extract_tools

  • 基础工具:匹配 ./tools/ 的导入,且导入的符号以 ToolTools 结尾,记录为 ToolSource::Base
  • 有条件工具:匹配 feature('...') 并与 Tool 相关的赋值语句,记录为 Conditional

4.4 启动阶段(extract_bootstrap_plan

cli.tsx 文件全文进行子串探测——比如查找 --versionstartupProfiler--daemon-worker 这些关键词。然后按固定顺序追加 BootstrapPhase 枚举项,最后再加上 MainRuntime。这种方法与真实运行时“检测到某功能就启用快速路径”的语义大致对应,但这只是一个粗粒度的静态近似分析。

5. 二进制入口:rusty-claude-cli

目前这个命令行工具只做三件事(代码在 main.rs 里):

子命令行为
(无参数)提示用户这只是基础版本,引导其查看 --help
dump-manifests对当前工作区调用 extract_manifest,打印出命令、工具、启动阶段的数量
bootstrap-plan打印 BootstrapPlan::claude_code_default()(在 runtime 里定义的一个完整阶段骨架)
--help显示使用说明

需要注意的是,bootstrap-plan 命令目前打印的是在 runtime 里写死的“全阶段列表”,而 dump-manifests 命令报告启动阶段数量时,使用的却是 extract_bootstrap_plan(cli.tsx) 这个启发式函数的结果子集。这两个命令的数据源目前还没有完全统一起来,这其实是“脚手架阶段”很常见的一种状态:一个命令展示的是“目标模型”,另一个展示的是“从上游扫描出来的近似结果”。后续可以把 bootstrap-plan 也改成走 extract_manifest 流程,或者让它同时输出两种结果,方便两边对比对齐。

6. 测试:既是“活文档”,也是回归护栏

compat-harness crate 里的集成测试(见 lib.rs 中的 #[cfg(test)] 模块)做得很实在:

  • 从本仓库的 fixture_paths() 里读取真实的 commands.tstools.ts 文件。
  • 断言抽取出来的清单不为空。
  • 断言一些已知的符号是存在的——比如 addDirreviewAgentToolBashTool,同时确认不会像字符串字面量那样错误地提取出 INTERNAL_ONLY_COMMANDS 这类内部专用标记。

这里有一个巧妙的设计:测试没有对整个列表做快照比对——因为上游任何小的改动都可能引发大量 diff。而是采用“非空”加上“关键锚点符号”这种最小化的护栏,在稳定性和维护成本之间取得了平衡。

7. 工作空间的工程质量

  • 使用 resolver = "2",统一了 edition、license 和 publish 配置。
  • 工作区级别的 lint 规则中,设置了 unsafe_code = forbid,这跟项目的安全文化保持一致,确保测试工具里也不会偷偷使用 unsafe 代码。

8. 对类似项目的可借鉴点

如果你也在做类似的工作——比如从 TypeScript 或 Ja vaScript 的大单体向 Rust 或 Go 迁移——那么下面这几点或许会有所帮助:

  1. 按现有接口切分 crate:先对齐产品里已经存在的模块名(比如 commands、tools、bootstrap),而不是按 Rust 教科书上的分层方式重新命名,这样能减少团队的沟通成本。
  2. 把“抽取器”和“领域类型”分开commandstools 这些 crate 只放注册表的数据结构;文件读取和启发式解析的逻辑单独放在 compat-harness 里。将来要更换解析实现时,下游的类型定义依然稳定。
  3. 让“证明”跑在持续集成里:通过一个小测试和一个类似 dump-manifests 的命令,把“还能读懂上游注册表”这件事变成一个合并的门禁,这比重写运行时本身要早得多。
  4. 给每个来源打上显式标签:像 BuiltinInternalOnlyFeatureGated 以及 BaseConditional 这样的枚举。这能让同一个符号在不同构建条件下的可见性有一个明确的表达方式,方便以后生成文档或者与新运行时做对齐。
  5. 接受第一版用“廉价的启发式方法”:在投入抽象语法树解析之前,用行级规则快速覆盖 80% 的清单内容。当然,要在文档里写清楚这种方法的脆弱之处。
  6. 坚持有向无环图的依赖关系:让 commandstools 互相不依赖,避免出现类型泥球。在大型项目上,这种依赖纪律带来的后期收益非常明显。
  7. 里程碑的话术要管理预期:README 里明确说明 harness-first 不等于特性完全对齐,这能有效管理相关人员的预期,避免测试工具被误认为“已经可以替代主程序了”。

9. 已知局限与后续方向

  • 这里面没有 Anthropic API、没有 QueryEngine、也没有 Ink/UI——Rust 层目前完全不参与主产品的实际运行路径。
  • 抽取过程中不验证 TypeScript 能否编译、import 是否成功解析、feature() 是否被死代码消除。
  • 正如前面第 5 节提到的,bootstrap-planextract_bootstrap_plan 的输出还没有统一起来,这是一个可以改进的工程细节。
  • 如果上游将来重命名了注册表文件,或者改成动态注册表的形式,就需要同步更新 UpstreamPaths 和解析规则。

10. 与仓库内其他文档的关系

  • rust/README.md:权威的简短版介绍,包含里程碑和命令行用法。
  • ARCHITECTURE.md:整个仓库(TypeScript + Rust)的架构总览。
  • 如果要扩展测试工具的功能,建议在 rust/README.md 里更新里程碑内容,而不是仅修改本文。

(本文内容基于 rust/README.mdrust/crates/*/src/lib.rsrusty-claude-cli/src/main.rsrust/Cargo.toml 整理。)

来源:https://juejin.cn/post/7623783517614030899
上一篇AI生活助手Ferret使用体验评测 下一篇AI智能写作助手如何重塑办公效率的未来
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
RAG四标融合企业知识资产体系四库协同GEO优化实践
AI教程 · 2026-07-01

RAG四标融合企业知识资产体系四库协同GEO优化实践

生成式AI正在彻底改写信息检索的底层逻辑。传统SEO依赖关键词堆砌和外链建设的策略,在大模型的内容采信规则下已经基本失效。取而代之的,是生成式引擎优化(GEO)。它不再关注外链数量,而是重点衡量你的知识是否结构化、证据链是否坚实、信源是否可靠——这些维度才是RAG(检索增强生成)架构真正看重的核心指

一个普通上班人分享WorkBuddy使用心得与真实体验
AI教程 · 2026-07-01

一个普通上班人分享WorkBuddy使用心得与真实体验

前言 最近我开始使用WorkBuddy——这是腾讯推出的一款AI办公工作台。差不多用了一周时间,趁印象还新鲜,把真实的使用感受记录下来,给还在犹豫的朋友做个参考。不吹不黑,只说实际体验。 初印象:不只是聊天机器人 之前用过不少AI工具,大多数就是个对话框,你问它答,答完就结束了。WorkBuddy不

AI幻觉变真功能实战教程:App Inventor 2视频录制拓展一周开发实录
AI教程 · 2026-07-01

AI幻觉变真功能实战教程:App Inventor 2视频录制拓展一周开发实录

先讲一个颇具戏剧性的开端。 这件事的开端颇显荒诞——有用户前来咨询,称AI Pro版的介绍中提到我们有一款“视频录制拓展”。团队全体成员都感到困惑,翻遍产品列表,发现根本不存在该组件。AI那种“一本正经胡说八道”的能力,这次确实让我们陷入尴尬。 按常理,此事到此便可结束——一句“抱歉,暂时没有这个拓

别再混淆OLAP和SQL-on-Hadoop两者查询本质不同
AI教程 · 2026-07-01

别再混淆OLAP和SQL-on-Hadoop两者查询本质不同

OLAP和SQL-on-Hadoop虽都使用SQL查询数据,但本质不同。SQL-on-Hadoop负责海量数据批量计算与ETL,查询速度秒级至分钟级;OLAP通过预聚合实现毫秒级多维分析,适合BI报表。两者在数据平台分工协作,前者是后厨加工,后者是前台快速服务。

GEO优化深度解析:AI偏好FAQ还是长文内容?
AI教程 · 2026-07-01

GEO优化深度解析:AI偏好FAQ还是长文内容?

在GEO优化中,AI对内容形式无统一偏好:FAQ在简单查询中引用率41%,长文在复杂查询中达58%。内容应基于用户意图选择形式,FAQ适配简单事实类问题,长文建立主题权威,两者互补而非替代。