重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
npx skills add https://github.com/workersio/spec --skill kani-proof在编写证明之前,请先验证工具是否已安装:
Kani: 运行 cargo kani --version。如果缺失:
cargo install --locked kani-verifier cargo kani setup
Linter(可选但推荐): 需要 Node.js。通过 npx -p @workersio/klint klint 运行。
Kani 是一个有界模型检查器——它探索边界内符号输入的所有可能值,使得证明是穷尽的(不像模糊测试那样采样)。
这些规则可以防止最常见的证明失败。违反其中任何一条都可能导致证明失败。
首次尝试时不要使用 #[kani::unwind] 或 #[kani::solver]。 完全省略这两个装饰器。只有在收到"展开断言"错误后才添加 #[kani::unwind(N)],并且只有在超时后才添加 #[kani::solver(kissat)]。Kani 的默认设置比猜测效果更好。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
内联断言目标属性,而不是通过辅助方法。 不要调用检查多个不变量或遍历集合的方法——它们会引入循环、额外的断言和不相关的失败点。直接读取结构体字段并自行编写比较:
// 错误 —— 辅助方法检查的内容超出了目标属性,并添加了循环 assert!(engine.check_all_invariants());
// 正确 —— 精确断言你要证明的内容,没有额外逻辑 assert!(engine.x.get() >= engine.y.get() + engine.z.get());
首先使用不带 kani::assume() 约束的 kani::any()。 只有在超时或内存不足后才添加假设约束。无约束的符号值通常比有界范围对求解器来说更容易处理。
仅通过公共 API 构建状态。 使用构造函数、add_user()、deposit() 等。切勿直接分配结构体字段——这会创建不可达的状态,导致虚假的失败。唯一的例外是没有设置器 API 的 vault 或类似的顶层字段。
栈分配,而非 Box。 使用 let mut engine = Engine::new(params) 而不是 Box::new(Engine::new(params))。Box 会给求解器增加堆跟踪开销。
小的配置参数。 如果构造函数接受一个控制循环的大小/容量参数(例如 max_accounts),则传递一个与分析代理找到的 #[cfg(kani)] 常量相匹配的小值(4–8)。
在选择工作流程之前,先对证明进行分类:
&mut self),或者模式是 P7(算术安全)/ P11(具体已知错误)/ 安全性 / 等价性,并且调用图中没有循环,且不需要构建多实体状态。对于简单证明,无需启动子代理,直接内联工作:
编写证明,直接从模式模板开始。阅读相应的 references/templates/ 文件(例如,P7 用 arithmetic-safety.rs,安全性/等价性用 safety.rs)并进行调整。从 references/templates/infrastructure.rs 开始,了解共享宏。
内联 Lint: 直接运行 linter:
npx -p @workersio/klint klint <file>
在继续之前修复所有错误或警告。
内联验证: 直接运行 Kani:
cargo kani --harness <harness_name>
如果失败,应用"诊断失败"中的修复方法并重新运行。
对于需要代码库分析、状态构建或迭代调试的复杂证明:
按照 references/agents/kani-analyzer-agent.md 启动一个 Explore 代理。它将返回循环边界、现有基础设施和状态构建模式。不要跳过此步骤。
使用代理的输出编写测试用例。从模式表中选择一个模式,并参考 references/proof-patterns.md 获取模板。模板文件位于 references/templates/ —— 阅读相应的模板并进行调整。从 infrastructure.rs 开始,了解共享宏(assert_ok!, assert_err!, 快照类型)。
编写证明后,在运行 cargo kani 之前,按照 references/agents/kani-linter-agent.md 启动一个 linter 代理。Linter 能在几秒钟内静态检测出 23 种常见反模式(矛盾的假设、缺少展开、空虚风险、过度约束的输入等)—— 远比长达数分钟的 cargo kani 运行快得多。
修复所有错误并处理警告,然后重新运行 linter,直到干净为止,再进入步骤 4。
Linter 检查干净后,按照 references/agents/kani-verifier-agent.md 启动一个验证器代理。它运行 cargo kani,解析输出,并返回结构化的诊断结果。
如果验证器报告 FAIL:
#[kani::unwind(N)],其中 N 来自错误信息#[kani::solver(kissat)],缩小范围迭代:根据诊断结果修复证明,重新运行 linter,然后重新运行验证器。不要提交未经验证的证明。
有关完整的 Kani API(合约、桩函数、具体回放、分区验证),请参阅 references/kani-features.md。
一个证明可以在没有证明任何内容的情况下报告 SUCCESS。当没有执行路径到达断言时会发生这种情况——因为操作对你的输入总是失败、假设矛盾、结果被丢弃,或者状态为空/平凡。
使用 kani::cover!(condition, "message") 检测 —— 如果 Kani 报告 UNSATISFIABLE,则该路径永远不会被执行。
通过显式处理结果来防止:
// 空虚的 —— 如果操作总是失败,则什么都不检查
if result.is_ok() { assert!(invariant); }
// 非空虚的 —— 如果操作不能成功,证明将失败
match result {
Ok(_) => { /* 断言属性 */ },
Err(_) => { kani::assert(false, "必须成功"); unreachable!() }
};
矛盾的假设: 如果每条路径都遇到 assume(false) 或所有 kani::cover!() 检查都是 UNSATISFIABLE,那么你的 kani::assume() 约束就是矛盾的——不存在有效的输入。移除约束并从无约束开始。
只有在你收到"展开断言"错误时才相关。添加 #[kani::unwind(N)],其中 N = 最大迭代次数 + 1。追踪调用图中的所有循环(目标 + 被调用者 + 构造函数)。检查是否有减少集合大小的 #[cfg(kani)] 常量。
参数驱动的循环: 如果构造函数根据配置参数进行循环(例如 for i in 0..capacity),则该参数必须很小(4–8)。当存在时,使用 #[cfg(kani)] 常量。
| Kani 输出 | 修复方法 |
|---|---|
unwinding assertion | 添加 #[kani::unwind(N)],其中 N = 循环次数 + 1 |
| 超时 / 求解器挂起 | 添加 kani::assume() 以缩小范围,尝试 #[kani::solver(kissat)] |
VERIFICATION:- FAILED | 使用 cargo kani -Z concrete-playback --concrete-playback=print --harness name |
| 内存不足 / 内存耗尽 | 减小状态大小,移除 Box,减少符号变量 |
所有路径上都是 assume(false) | 移除 kani::assume() 约束——它们是矛盾的 |
VERIFICATION:- SUCCESSFUL | 检查 kani::cover!() 语句是否 SATISFIED(非空虚性) |
迭代方法: 从简单开始(无装饰器、无约束输入、通过 API 构建状态)→ 仅在超时/内存不足时添加约束 → 仅在展开错误时添加展开 → 仅在超时时切换求解器。
Kani 有实际的限制。在以下情况下使用 Kani 会浪费大量时间在注定失败的证明上:
| 情况 | Kani 为何难以处理 | 更好的工具 |
|---|---|---|
| 浮点算术 | 不支持符号 f32/f64 | proptest, bolero |
| 异步代码 | 运行时未建模 | tokio::test + proptest |
| 网络/IO | 无法模拟系统调用 | 集成测试 |
| 无合约的深度递归 | 无界展开 | 函数合约或 proptest |
| 非常大的状态(>1000 个元素) | 求解器超时 | 使用 #[cfg(kani)] 缩小范围或进行模糊测试 |
完整的模式文档请参阅 references/proof-patterns.md。模板文件位于 references/templates/ —— 阅读相应的模板并进行调整。从 infrastructure.rs 开始,了解共享宏(assert_ok!, assert_err!, 快照类型)。
| 模式 | 何时使用 | 证明内容 |
|---|---|---|
| 守恒性 | 移动、创建或销毁数量 | 会计等式保持不变 |
| 框架 / 隔离性 | 针对多实体系统中的单个实体 | 旁观者实体保持不变 |
| 不变量保持性 | 任何状态突变 | 规范不变量在操作前后均成立 |
| 错误路径 | 输入验证 / 前置条件 | 特定错误 + 状态完全不变 |
| 单调性 | 计数器、时间戳、累加器 | 值仅朝一个方向移动 |
| 幂等性 | 结算、同步、重新计算 | 应用两次 = 应用一次 |
| 算术安全性 | 数值计算 | 无溢出/下溢/除零 |
| 访问控制 | 特权操作 | 拒绝未经授权的调用者 |
| 状态机 | 生命周期转换 | 仅发生有效转换 |
| 归纳增量 | 核心会计(最强形式) | 等式在原始基元上成立 |
| 生命周期 / 序列 | 多步骤用户流程 | 属性在链式操作中保持成立 |
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
// 不要使用 #[kani::unwind] —— 仅在收到展开断言错误后添加
// 不要使用 #[kani::solver] —— 仅在收到超时后添加
fn proof_name() {
// 1. 通过公共 API 构建状态(不要直接修改字段)
// 2. 符号输入:使用 kani::any(),不要使用 kani::assume() 约束
// 3. 调用函数,显式处理结果(不要用 if result.is_ok())
// 4. 仅使用原始字段访问断言目标属性
// (不要使用 check_conservation 或其他聚合方法)
// 5. 使用 kani::cover!() 确保非空虚性
}
}
Explore 代理会识别需要的内容。常见的准备工作:
#[cfg(kani)] const MAX_ITEMS: usize = 4; —— 减小集合大小[workspace.metadata.kani] flags = { tests = true }#[cfg(kani)] extern crate kani;每周安装次数
57
代码仓库
GitHub Stars
9
首次出现
2026年3月5日
安全审计
安装于
claude-code56
opencode53
gemini-cli53
github-copilot53
codex53
amp53
Before writing proofs, verify tools are installed:
Kani: Run cargo kani --version. If missing:
cargo install --locked kani-verifier cargo kani setup
Linter (optional but recommended): Requires Node.js. Runs via npx -p @workersio/klint klint.
Kani is a bounded model checker — it explores ALL possible values of symbolic inputs within bounds, making proofs exhaustive (not sampled like fuzzing).
These rules prevent the most common proof failures. Violating any one will likely cause the proof to fail.
No#[kani::unwind] or #[kani::solver] on first attempt. Omit both decorators entirely. Only add #[kani::unwind(N)] after getting an "unwinding assertion" error, and only add #[kani::solver(kissat)] after a timeout. Kani's defaults work better than guessing.
Assert the target property inline, not via helper methods. Do not call methods that check multiple invariants or iterate over collections — they introduce loops, extra assertions, and unrelated failure points. Read the struct fields directly and write the comparison yourself:
// WRONG — helper checks more than the target property, adds loops assert!(engine.check_all_invariants());
// RIGHT — asserts exactly what you're proving, no extra logic
assert!(engine.x.get() >= engine.y.get() + engine.z.get());
3. Usekani::any() without kani::assume() bounds first. Only add assume constraints after a timeout or OOM. Unconstrained symbolic values are often easier for the solver than bounded ranges.
Build state through public API only. Use constructors, add_user(), deposit(), etc. Never assign struct fields directly — it creates unreachable states that cause spurious failures. The only exception is vault or similar top-level fields with no setter API.
Stack allocation, not Box. Use let mut engine = Engine::new(params) not Box::new(Engine::new(params)). Box adds heap tracking overhead to the solver.
Small config parameters. If the constructor takes a size/capacity parameter that controls a loop (e.g. max_accounts), pass a small value (4–8) that matches #[cfg(kani)] constants found by the analyzer agent.
Before choosing a workflow, classify the proof:
&mut self), OR pattern is P7 (Arithmetic Safety) / P11 (Concrete Known-Bad) / Safety / Equivalence, AND no loops in the call graph, AND no multi-entity state construction needed.For simple proofs, work inline without spawning sub-agents:
Write the proof directly from the pattern template. Read the appropriate references/templates/ file (e.g., arithmetic-safety.rs for P7, safety.rs for Safety/Equivalence) and adapt it. Start with references/templates/infrastructure.rs for shared macros.
Lint inline: Run the linter directly:
npx -p @workersio/klint klint <file>
Fix any errors or warnings before proceeding.
Verify inline: Run Kani directly:
cargo kani --harness <harness_name>
If it fails, apply the fixes from Diagnosing Failures and re-run.
For complex proofs requiring codebase analysis, state construction, or iterative debugging:
Spawn an Explore agent following references/agents/kani-analyzer-agent.md. It will return loop bounds, existing infrastructure, and state construction patterns. Do not skip this.
Use the agent's output to write a harness. Select a pattern from the pattern table and see references/proof-patterns.md for templates. Template files are available in references/templates/ — read the appropriate template and adapt it. Start with infrastructure.rs for shared macros (assert_ok!, assert_err!, snapshot types).
After writing the proof and before running cargo kani, spawn a linter agent following references/agents/kani-linter-agent.md. The linter statically detects 23 common anti-patterns (contradictory assumes, missing unwind, vacuity risks, over-constrained inputs, etc.) in seconds — far faster than the minutes-long cargo kani run.
Fix all errors and address warnings, then re-run the linter until clean before proceeding to Step 4.
After the linter is clean, spawn a verifier agent following references/agents/kani-verifier-agent.md. It runs cargo kani, parses the output, and returns a structured diagnosis.
If the verifier reports FAIL:
#[kani::unwind(N)] with N from the error#[kani::solver(kissat)], narrow rangesIterate: fix the proof based on the diagnosis, re-run the linter, then re-run the verifier. Do not submit a proof that has not been verified.
See references/kani-features.md for the full Kani API (contracts, stubbing, concrete playback, partitioned verification).
A proof can report SUCCESS while proving nothing. This happens when no execution path reaches assertions — because the operation always fails for your inputs, assumptions are contradictory, results are discarded, or state is empty/trivial.
Detect with kani::cover!(condition, "message") — if Kani reports UNSATISFIABLE, that path is never taken.
Prevent by handling results explicitly:
// VACUOUS — if operation always fails, nothing is checked
if result.is_ok() { assert!(invariant); }
// NON-VACUOUS — proof fails if operation can't succeed
match result {
Ok(_) => { /* assert properties */ },
Err(_) => { kani::assert(false, "must succeed"); unreachable!() }
};
Contradictory assumptions: If every path hits assume(false) or all kani::cover!() checks are UNSATISFIABLE, your kani::assume() constraints are contradictory — no valid inputs exist. Remove constraints and start unconstrained.
Only relevant if you get an "unwinding assertion" error. Add #[kani::unwind(N)] where N = max_iterations + 1. Trace ALL loops in the call graph (target + callees + constructors). Check for #[cfg(kani)] constants that reduce collection sizes.
Parameter-driven loops: If a constructor loops over a config param (e.g. for i in 0..capacity), that param must be small (4–8). Use #[cfg(kani)] constants when they exist.
| Kani Output | Fix |
|---|---|
unwinding assertion | Add #[kani::unwind(N)] with N = loop_count + 1 |
| Timeout / solver hang | Add kani::assume() to narrow ranges, try #[kani::solver(kissat)] |
VERIFICATION:- FAILED | Use cargo kani -Z concrete-playback --concrete-playback=print --harness name |
| OOM / out of memory | Reduce state size, remove Box, fewer symbolic variables |
| on all paths |
Iterative approach: Start SIMPLE (no decorators, unconstrained inputs, API-built state) → add constraints only on timeout/OOM → add unwind only on unwinding errors → switch solver only on timeout.
Kani has real limits. These situations will waste significant time on doomed proofs:
| Situation | Why Kani Struggles | Better Tool |
|---|---|---|
| Floating-point arithmetic | No symbolic f32/f64 | proptest, bolero |
| Async code | Runtime not modeled | tokio::test + proptest |
| Network/IO | Cannot model syscalls | Integration tests |
| Deep recursion w/o contracts | Unbounded unrolling | Function contracts or proptest |
| Very large state (>1000 elements) | Solver timeout | Narrow with #[cfg(kani)] or fuzz |
See references/proof-patterns.md for full pattern documentation. Template files are available in references/templates/ — read the appropriate template and adapt it. Start with infrastructure.rs for shared macros (assert_ok!, assert_err!, snapshot types).
| Pattern | When to Use | What It Proves |
|---|---|---|
| Conservation | Moves, creates, or destroys quantities | Accounting equation preserved |
| Frame / Isolation | Targets one entity in multi-entity system | Bystander entities unchanged |
| INV Preservation | Any state mutation | Canonical invariant holds before and after |
| Error Path | Input validation / preconditions | Specific error + state completely unchanged |
| Monotonicity | Counters, timestamps, accumulators | Value only moves in one direction |
| Idempotency | Settlement, sync, recompute | Applying twice = applying once |
| Arithmetic Safety | Numeric computation | No overflow/underflow/div-by-zero |
| Access Control | Privileged operations | Unauthorized callers rejected |
| State Machine |
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
// NO #[kani::unwind] — only add after getting unwinding assertion error
// NO #[kani::solver] — only add after getting timeout
fn proof_name() {
// 1. Build state through public API (NOT field mutation)
// 2. Symbolic inputs: kani::any() with NO kani::assume() bounds
// 3. Call function, handle result explicitly (no if result.is_ok())
// 4. Assert ONLY the target property using raw field access
// (NOT check_conservation or other aggregate methods)
// 5. kani::cover!() for non-vacuity
}
}
The Explore agent identifies what's needed. Common preparations:
#[cfg(kani)] const MAX_ITEMS: usize = 4; — reduce collection sizes[workspace.metadata.kani] flags = { tests = true } in Cargo.toml#[cfg(kani)] extern crate kani; at crate rootWeekly Installs
57
Repository
GitHub Stars
9
First Seen
Mar 5, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
claude-code56
opencode53
gemini-cli53
github-copilot53
codex53
amp53
Lark Mail CLI 使用指南:邮件管理、安全规则与自动化工作流
43,200 周安装
assume(false)Remove kani::assume() constraints — they're contradictory |
VERIFICATION:- SUCCESSFUL | Check kani::cover!() statements are SATISFIED (non-vacuity) |
| Lifecycle transitions |
| Only valid transitions occur |
| Inductive Delta | Core accounting (strongest form) | Equation holds with raw primitives |
| Lifecycle / Sequence | Multi-step user flows | Properties hold through chained operations |