Bitwarden Server 的 bump-rust-sdk 技能评测集:用 5 个行为用例守住 Rust SDK 升级的关键决策
【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server
导读
本文以 .claude/skills/bump-rust-sdk/evals/README.md 为主线,完整讲解 Bitwarden Server 仓库中bump-rust-sdkClaude Skill 的评测(evals)体系:如何用 5 个行为测试用例覆盖该技能最实质性的决策——NPM 版本到 git 提交的映射、拒绝已废弃的 GitHub Actions run-number 方案、MSRV/工具链升级、定点cargo update -p、以及破坏性变更修复。读完本文,你将掌握这套评测集的用例设计、通过标准(expectations)、消融(ablation)记录方法,并理解其背后的 RustSdk 升级全流程原理与仓库源码佐证。
一、背景:bump-rust-sdk 技能与它的评测集
.claude/skills/bump-rust-sdk是仓库中为 AI 助手(Claude)定义的一个技能目录,核心文件为 SKILL.md。它的职责是:把服务器端 util/RustSdk/rust/Cargo.toml 中bitwarden-crypto的 git rev 固定值,升级到某个bitwarden/clients生产版本所对应的提交,并处理升级过程中的破坏性变更与验证。
该技能对应的评测集放在 .claude/skills/bump-rust-sdk/evals/ 下,README 明确说明:
Behavior test cases for the
bump-rust-sdkskill, in theskill-creatorschema.
即:这是按照skill-creator模式编写的行为测试用例,用于验证技能在被调用时是否做出正确决策。evals.json中存放了 5 个用例,每个用例的expectations就是通过标准;其中用例 4 和 5 还带有notes,记录各自的消融实验结果(earned vs. borderline)。
评测的运行方式为:在 Benchmark 模式下通过/skill-creator:skill-creator命令执行,分别对比“带技能(with-skill)”与“不带技能(without-skill)”的表现。
二、RustSdk 在仓库中的真实地位
在深入用例之前,有必要先明确 RustSdk 的角色,因为评测集的每一个决策点都建立在它的定位之上。从 SKILL.md 可以确认:
util/RustSdk/rust/Cargo.toml以 git rev 方式固定bitwarden-crypto(来源为bitwarden/sdk-internal仓库),需要周期性升级以跟随bitwarden/clients发布;- RustSdk 是 Seeder 的测试基础设施(位于
util/而非生产代码src/),它通过 FFI 为 C# Seeder 提供字段级加密能力(encrypt_string/decrypt_string/encrypt_fields),用来生成密码学上正确的 Protected Data,供集成测试使用; EncryptPropertyAttribute(见 util/Seeder/Attributes/EncryptPropertyAttribute.cs)驱动哪些字段需要被加密。
仓库源码印证了这一点:util/Seeder/Factories/CipherEncryption.cs中通过RustSdkService.EncryptFields(...)/EncryptFieldsWithCipherKey(...)调用 Rust FFI,CollectionSeeder、FolderSeeder、ProjectSeeder等均通过RustSdkService.EncryptString(...)加密名称字段。因此升级bitwarden-cryptorev 是否准确,直接决定测试数据的密码学正确性。
三、5 个评测用例总览
evals.json(.claude/skills/bump-rust-sdk/evals/evals.json)中的 5 个用例覆盖了该技能的核心决策路径:
| 用例 ID | 评测主题 | 关键决策点 |
|---|---|---|
| 1 | NPM→git-SHA 映射 | 从发布的 WASM 中读取内嵌main (<short-sha>)并git rev-parse,而非查询 Actions run number |
| 2 | 抵制废弃的 run-number 方法 | 纠正“查询 GitHub Actions run number 取 head_sha”的错误思路 |
| 3 | MSRV/工具链升级 | 将rust-toolchain.toml的 channel 提升到工作区rust-version(MSRV),而非 dev 工具链 |
| 4 | 定点cargo update -p | 使用cargo update -p bitwarden-crypto而非裸cargo update |
| 5 | 破坏性变更修复 | SymmetricCryptoKey::make_aes256_cbc_hmac_key()变为pub(crate)后的编译修复 |
四、用例 1:NPM→git-SHA 映射(核心决策)
4.1 用例内容
最新客户端生产版本将
@bitwarden/sdk-internal固定在0.2.0-main.841。请把该 NPM 版本映射到util/RustSdk/rust/Cargo.toml中应固定的bitwarden-cryptogit rev,并解释推导过程。
期望输出:解析到c5d5bba159bd222321f3ecfd90f5ae6192c2c8eb——通过读取发布 WASM 中内嵌的main (<short-sha>)字符串,再执行git rev-parse得到;而不是通过查询 GitHub Actions run number 或关联时间戳。
通过标准(expectations):
- 最终 git rev 为
c5d5bba159bd222321f3ecfd90f5ae6192c2c8eb; - 方法必须包含“下载 npm tarball → 在
bitwarden_wasm_internal_bg.wasm中 grepmain (<short-sha>)→ 对 short sha 执行git rev-parse”; - 响应中不得出现通过 GitHub Actions run-number 查询(如
gh api .../actions/workflows/.../runs取head_sha)来映射版本的方式; - 响应中不得提出
c9f9dba或1e45444作为 rev(它们分别对应0.2.0-main.842和0.2.0-main.840)。
4.2 为什么不能依赖 run number 或时间戳
SKILL.md 给出了明确的技术依据:
- 客户端以 npm 形式消费 sdk-internal(
@bitwarden/sdk-internal,如0.2.0-main.841),而服务器固定的是 git rev; .NNN后缀是一个来自私有 Azure 发布任务的不透明计数器,不是GitHub Actions 的run_number,也不与main分支的提交一一对应;- 仓库已验证过:
841→c5d5bba、842→c9f9dba、840→1e45444,三者之间没有时间相关性——靠 run number 或时间戳推断 SHA 都会选错提交。
正确的做法是读取构建时写入发布 WASM 的提交信息:
# <VERSION> 是客户端 release tag 中的 npm 版本,例如 0.2.0-main.841 cd "$(mktemp -d)" curl -sSL "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-<VERSION>.tgz" -o pkg.tgz tar xzf pkg.tgz grep -ao "main ([0-9a-f]\{7\})" package/bitwarden_wasm_internal_bg.wasm | sort -u # -> main (c5d5bba) cd /path/to/sdk-internal && git rev-parse c5d5bba # -> 需要在 Cargo.toml 固定的完整 rev这里有一个关键设计考量:由于@bitwarden/commercial-sdk-internal与开源 tarball 打包的是同一个提交,因此开源 tarball 内嵌的 SHA 对两者都具有权威性——这正是该方法可被自动化的根本保证。
仓库当前状态可以交叉验证:util/RustSdk/rust/Cargo.toml中bitwarden-crypto的固定值正是c5d5bba159bd222321f3ecfd90f5ae6192c2c8eb,与用例 1 的期望答案完全一致。
五、用例 2:抵制废弃的 run-number 方法
5.1 用例内容
为了升级 RustSdk,我打算直接查询 sdk-internal 发布 841 的 GitHub Actions run number 并取其 head_sha。请带我按这种方式操作。
期望输出:纠正该方法——run-number 方式已被移除/不可靠,且发布计数器并不与提交一一对应;应引导用户改从已发布 WASM tarball 中读取内嵌的main (<short-sha>)。
通过标准:
- 明确指出 GitHub Actions run-number 方法对该映射已废弃/移除且不可靠;
- 引导改读 npm tarball WASM 中内嵌的
main (<short-sha>); - 不得产出把 run-number 方法当作获取 rev 途径的分步操作指导。
5.2 设计意图
这个用例考验的是技能的“纠错”能力:当用户主动提出一条错误但看似可行的路径时,技能必须基于对映射原理的准确理解进行拦截与重定向,而不是顺着用户的思路给出“看似可执行”的指令。它与用例 1 形成一正一反的组合,从“正确做法”和“错误做法”两个方向同时锁定映射决策的边界。
六、用例 3:MSRV/工具链升级
6.1 用例内容
我正在把 bitwarden-crypto 升级到一个其 sdk-internal 工作区 rust-version 为 1.88.0 的 rev。
util/RustSdk/rust-toolchain.toml当前 channel 为1.87.0。工具链该怎么处理?
期望输出:把rust-toolchain.toml的 channel 提升到1.88.0以匹配 MSRV(工作区rust-version),不是sdk-internal 的 dev 工具链;跳过该升级会导致 “requires rustc X or newer” 的构建失败。
通过标准:
- 指示将
rust-toolchain.toml的 channel 提升到1.88.0; - 说明 channel 应对齐 MSRV(工作区
rust-version),而非 sdk-internal 的 dev 工具链; - 解释跳过 MSRV 升级会触发 “requires rustc X or newer” 构建失败。
6.2 原理与仓库现状
SKILL.md 的“Apply”步骤对此有完整规定:对比目标 rev 工作区的rust-version与util/RustSdk/rust-toolchain.toml;若 MSRV 高于当前 channel,则将 channel 提升到MSRV(而不是 sdk-internal 的 dev 工具链),否则构建报 “requires rustc X or newer”。
这里的关键陷阱在于:sdk-internal 的开发者可能使用比 MSRV 更新的本地工具链,若盲目对齐 dev 工具链版本,会把不必要的升级引入服务器构建。正确的基准永远是工作区声明的rust-version。
值得注意的是,仓库当前 rust-toolchain.toml 的 channel 已是1.94.1——这是后续多次升级累积的结果(2026 年 6 月那次从1.87.0升到1.88.0之后又继续演进)。用例 3 描述的是该技能设计时所针对的典型场景,其决策原则(对齐 MSRV 而非 dev 工具链)在每次升级中都同样适用。
七、用例 4:定点cargo update -p
7.1 用例内容
我已经更新了
util/RustSdk/rust/Cargo.toml中的 bitwarden-crypto rev。对于新 rev,应如何重新解析 Cargo.lock?
期望输出:使用定点形式cargo update -p bitwarden-crypto,而不是裸cargo update——后者会搅动无关 crate 并让 lockfile 差异无谓膨胀。
通过标准:
- 推荐
cargo update -p bitwarden-crypto(定点形式); - 指出裸
cargo update会搅动无关 crate / 不必要地膨胀 lockfile diff。
7.2 消融记录:为什么这条指令是“承重墙”
该用例带有notes,记录了一次于 2026-07-01 执行的消融实验(每个配置 3 次,盲评):
EARNED — kept.Baseline(无技能)本身 2/2 能稳定偏好
-p,但移除这条指令后,with-skill 的通过率跌到约 33%(0/2、0/2、2/2):当技能在场时,Step 5 的cargo build变成了干扰项,模型会推荐“以构建驱动重新解析”而不是定点更新。这条指令是承重墙,因为它抵消的是技能自身的引导偏差,而非 baseline 的失败。
这是一个非常值得借鉴的评测洞见:评测用例的价值不能只看 baseline 是否失败。有些指令之所以必须保留,是因为技能内部其他步骤(这里是cargo build)会产生误导性的强信号,评测需要验证技能能否抵抗自己的内部干扰。这种“消融后 with-skill 反而退化”的现象,是衡量用例是否 load-bearing 的关键判据。
八、用例 5:破坏性变更修复
8.1 用例内容
在旧 rev 与新 rev 之间,
SymmetricCryptoKey::make_aes256_cbc_hmac_key()变成了pub(crate)。RustSdk 在多个位置调用了它。影响是什么?如何修复?
期望输出:判定为破坏性变更(调用点无法编译),修复方式是改用make(SymmetricKeyAlgorithm::Aes256CbcHmac),并导入SymmetricKeyAlgorithm枚举。
通过标准:
- 识别受影响调用点将无法编译(是破坏性变更,不是警告);
- 给出修复:替换为
SymmetricCryptoKey::make(SymmetricKeyAlgorithm::Aes256CbcHmac); - 说明必须导入
SymmetricKeyAlgorithm枚举。
8.2 仓库源码印证
当前仓库源码已经完成了这次迁移,可以交叉验证修复后的最终形态:
- util/RustSdk/rust/src/lib.rs 第 102 行:
let key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::Aes256CbcHmac);; - util/RustSdk/rust/src/cipher.rs 第 273 行(
encrypt_fields_with_cipher_key_internal中生成每 cipher 独立密钥):let cipher_key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::Aes256CbcHmac);; provider.rs、attachment.rs中的密钥构造同样统一为make(SymmetricKeyAlgorithm::Aes256CbcHmac)。
同时,references/api-surface.md(该文件声称由源码自动生成,记录 RustSdk 从bitwarden-crypto导入的全部类型、trait 与函数)在SymmetricCryptoKey条目下已不再列出make_aes256_cbc_hmac_key(),而 SKILL.md 的流程要求升级后重新生成该清单,二者相互印证。
8.3 消融记录:borderline 用例为什么保留
用例 5 的notes记录了 2026-07-01 的消融结果(每个配置 3 次,盲评):
borderline:通过率与 baseline 持平(with-skill 78%、baseline 78%),各出现一次 flaky。但保留,理由是:(1) baseline 只能靠回忆通用的
make(SymmetricKeyAlgorithm::Aes256CbcHmac)API 才能答对,而该 API 是版本相关的、会随 rev 漂移——baseline 已出现一次 flaky 到错误猜测的 API(generate/try_from),一个错误的密钥构造器悄悄进入 Seeder 正是这个用例要防的失败;(2) with-skill 的正确回答来自 worked example,即 references/examples/2026-06-bump.md——这种按需读取的参考内容几乎不占常驻上下文,却固定了经过验证的具体迁移方案,并为下次升级示范了处理破坏性变更的方法;(3) 仅凭通过率持平就裁剪用例已经失败过一次(即用例 4)。
这里揭示的评测原则是:即使通过率与 baseline 持平,只要用例守住的是“版本漂移导致的错误 API 猜测”这类高风险失败,就值得保留——测试数据密码学正确性的底线,不能交给模型对特定版本的记忆。
九、评测集背后的完整升级流程
5 个用例各自锚定了升级流程中的一个决策点。完整流程见 SKILL.md:
- 识别目标——从 clients 最新
web-v*release tag 出发,取其 npm 版本:gh release list --repo bitwarden/clients --limit 5 | grep web-v git -C /path/to/clients show <tag>:package.json | grep sdk-internal # -> 0.2.0-main.841 - 映射 npm → git SHA——即用例 1、2 覆盖的嵌入式 SHA 方法;
- 分析破坏性变更——将每个提交与
references/api-surface.md交叉比对,重点检查类型重命名、函数移除/废弃、签名变更与 trait 变化:cd /path/to/sdk-internal git log --oneline <old-rev>..<new-rev> -- crates/bitwarden-crypto git diff <old-rev>..<new-rev> -- crates/bitwarden-crypto/src/keys/mod.rs crates/bitwarden-crypto/src/lib.rs - 应用变更——更新
Cargo.toml中的 rev;MSRV 检查(用例 3);执行cargo update -p bitwarden-crypto(用例 4);修复编译错误,对新增的废弃 API 加#[allow(deprecated)]并附 why 注释; - 构建与验证:
cd util/RustSdk/rust cargo build && cargo test # 关卡测试:encrypt_string_decrypt_string_roundtrip cargo fmt --check git diff ../NativeMethods.g.cs # 必须保持不变 dotnet test test/SeederApi.IntegrationTest/并检查
Cargo.lockdiff 中是否出现意外的传递性加密 crate(rsa、aes、sha2); - 人工验证(仅限人类执行,AI 只负责呈现)——人工播种并确认 Protected Data 在 web 客户端可解密。
其中“验证”一环同样有源码依据:util/RustSdk/rust/src/cipher.rs的测试encrypt_string_decrypt_string_roundtrip(第 308 行)验证了 EncString 往返,RustSdkCipherTests(位于 test/SeederApi.IntegrationTest/)则从 C# 侧覆盖 FFI 行为。
十、与 worked example 的呼应:2026 年 6 月那次升级
references/examples/2026-06-bump.md 记录了确立“嵌入式 SHA 映射”的那次真实升级——在此之前,旧的 run-number 方法和时间戳关联法都选错了提交:
- 旧 rev:
abba7fdab687753268b63248ec22639dff35d07c(2026-02-05,bitwarden-crypto2.0.0); - 目标:web v2026.6.3 / desktop、browser、CLI v2026.6.0;
- NPM 版本:
0.2.0-main.841;新 rev:c5d5bba159bd222321f3ecfd90f5ae6192c2c8eb(bitwarden-crypto3.0.0)。
该文档明确记录:时间戳启发式曾错误地选中c9f9dba(实际对应0.2.0-main.842),而0.2.0-main.840内嵌1e45444——证明发布计数器并不按时间跟踪提交。这与评测用例 1 的期望答案、用例 2 的纠错场景完全互文,也解释了为什么评测集把“读取 WASM 内嵌 SHA”作为不可妥协的决策。
此外,这次升级发现了两个破坏性变更(正是用例 3、5 的原型):SymmetricCryptoKey::make_aes256_cbc_hmac_key()变pub(crate)(5 个调用点编译失败),以及工作区 MSRV 从1.85.1升至1.88.0。Cargo.lock 层面还出现了一批 major 升级(aes0.8→0.9、sha20.10→0.11、rsa0.9→0.10.0-rc、新增后量子ml-dsa),并新增了非可选依赖bitwarden-api-key-connector→reqwest/hyper传递 HTTP 栈。最终 Rust 13 个单测、C# 177 个 SeederApi.IntegrationTest(含 17 个RustSdkCipherTests)全部通过。
十一、评测集设计模式总结
纵观这套 evals,可以提炼出对同类“升级类技能”评测具有普适性的设计方法:
- 围绕实质性决策而非步骤完整度设计用例——5 个用例各自对应一个“做错会造成真实损失”的决策点(选错提交、用错方法解析 lockfile、漏升 MSRV、误用被移除的 API);
- 正反成对——用例 1 教正确做法,用例 2 专门拦截错误做法,防止技能在用户引导下“顺着错路给出可执行指令”;
- 期望输出描述“方法而非结果”——expectations 不仅检查最终 rev 正确,还明确禁止 run-number 查询路径,防止模型碰巧答对但方法错误;
- 用消融记录判断用例的承重性——用例 4 展示了“baseline 能答对、移除指令后 with-skill 反而退化”的承重墙特征;用例 5 展示了“通过率持平但守住版本漂移高风险”的保留理由;
- 评测与文档、示例形成闭环——SKILL.md、references/api-surface.md、references/examples/2026-06-bump.md 与 evals 相互印证,让每次升级既能被验证、也能被追溯。
对于需要长期维护“升级依赖/同步上游”类技能的团队,这套“行为用例 + 正反用例 + 消融记录”的组合,是一个可以直接借鉴的评测模板。
【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考