tombaldwin/candor-rust
GitHub: tombaldwin/candor-rust
一个 Rust 类型感知的能力/副作用检查器,通过传递性跨 crate 分析在 CI 门禁中强制执行架构与功能边界。
Stars: 0 | Forks: 0
# candor
.scan.json (or --json to stdout)
# (a workspace root: one report per member, same prefix)
```
它遍历 crate 的 `.rs` 文件,使用 [`syn`](https://docs.rs/syn) 解析它们,解析 `use`-别名
调用路径,并通过**与 lint 使用的相同的 [`candor-classify`](crates/candor-classify)** 对它们进行分类 —— 单一的事实来源,因此两个后端在什么算作副作用的问题上不会产生偏差。它只需要
稳定的工具链,因此它可以在任何 `cargo` 运行的地方运行(没有 nightly 的 CI,上锁的
机器)。在完整的 candor 安装中,它也是 `cargo candor scan`。
**默认稳定 —— 你无需选择后端。** 只读查询 (`show`/`where`/`callers`/`map`/
`audit`) 和 [Claude Code 回执](integrations/claude-code/) 在安装了 nightly lint 时
优先使用它(为了可靠性契约),并在未安装时**自动回退到稳定版扫描器** —— 因此 candor 可以在任何机器上零安装运行,当使用语法路径时,回执会显示 `· stable backend`。强制执行 (`guard`/`policy`/`snapshot`/`diff`) 仍然需要 lint:
阻止 PR 需要可靠性保证。
权衡是**精度,如实陈述**。扫描器是语法层面的,所以它看到的是*写出来的*内容,而不是
编译器*解析的*内容。它捕获路径限定的副作用调用 (`std::fs::read`, `Command::new`,
`reqwest::Client::execute`)、`use`-别名、crate 内传递性传播,以及**局部 trait 分发**
(`&dyn Store`/`impl Store`/`S: Store` 接收器解析为 trait 的本地实现者 ——
语法层面的 CHA,像 JVM 引擎一样有边界 —— 或者当 trait 没有可见的 impl 或实现太多时,如实读取为 `Unknown`)。对于依赖项,回执**指出分类器无法看到的内容**(κ-覆盖率账本:`κ doesn't know N dependencies this code calls into…`),而 `--deps` **关闭它**:
扫描整个 `Cargo.lock` 树一次(未构建的注册表源,实测约 0.23秒/依赖),根扫描通过报告串联起来 —— 副作用跨越每个 crate 边界,而分类器不需要知道这些 crate(规范 §2 报告串联)。它**会遗漏** —
*默默地*,不发出 `Unknown` —— 仅通过非路径限定接收器上的方法调用、通过外部 trait发、闭包/函数指针、宏以及通过稳定标识进行的跨 crate 传播而触及的副作用。因此在重度依赖解析的代码上,与 lint 相比,它**少报**。将 `scan` 用于零摩擦的分类和稳定版上的 CI;当你需要可靠性契约
(`Unknown` 过近似、一致性、policy/guard 门禁)时使用 nightly lint。
默认情况下,`scan` 仅报告 crate 的**库/二进制**源码 —— 它跳过 `tests/`, `benches/`,
`examples/`, `build.rs`, 和 `#[cfg(test)]` 模块,因此报告展示的是 *crate* 的行为,而不是其测试套件的行为(`--include-tests` 会保留它们)。一项[对 35 个真实 crate 的校准](eval/calibration/CALIBRATION.md)
发现,启用此功能后,扫描器在库代码中**没有误报** —— 它报告的每个副作用都是真实的;它唯一的错误是通过 FFI、方法分发和宏少报了(这是 lint 的工作)。例如,它正确捕获了 chrono 读取 `/etc/localtime`+`$TZ` 和 `which` 解析 `$PATH`,
并诚实地在 reqwest 上显示 `Net: 0`(其 socket I/O 隐藏在 hyper 解析的方法调用后面)。
## 快速开始(人类)
执行 `install.sh` 之后,在任何 Rust 项目中使用包装器(它会自我修复 —— 如果 dylib 丢失,它会自动重建):
```
cargo candor scan # STABLE backend: produce the report on stock cargo (no nightly)
cargo candor audit # at-a-glance effect profile of the whole project (nightly lint)
cargo candor audit --all # the full per-function lint (spans in context)
cargo candor snapshot .candor/baseline # write a JSON report
cargo candor guard .candor/baseline # fail on functions that gained an effect
cargo candor diff .candor/baseline # describe the per-function effect delta (--json)
cargo candor watch # keep the report fresh in the background → instant `diff`
cargo candor show my_function # a function's effects, instant (read from the report)
cargo candor where Net # which functions perform an effect, instant
cargo candor callers my_function # which functions call this one, instant (who depends on it)
cargo candor explain my_function # trace WHY a function has each effect (the call path)
cargo candor containment [baseline] # effect-leakage diagnostic; with a baseline, a CI ratchet
cargo candor reachable # what the program does at runtime (union over entry points)
cargo candor path my_fn Net # the call chain by which a fn comes to perform an effect
cargo candor impact my_fn # blast radius: transitive callers + downstream entry points
cargo candor policy .candor/policy # enforce effect boundaries (deny/pure rules)
cargo candor risk # heuristic: effects on caller-derived input (advisory)
cargo candor strict my_module # conformance, scoped to a module
cargo candor no-ambient my_module # flag direct ambient-authority use
```
`cargo candor audit` 将项目的 crate 聚合到一个单屏配置文件中 —— 有多少
函数执行了每种副作用,哪些函数调用了 candor 无法解析的内容,是否有未校准的
依赖项,以及对外部世界影响最广的函数:
```
candor @62a9383
143 effectful functions · 7 pgman.Executable · 136 pgman.Rlib
effects 56 Db · 53 Clock · 47 Log · 37 Env · 27 Fs · 23 Exec · 21 Clipboard · 18 Net
broadest effect surface
app::App::run { Clipboard Clock Db Env Exec Fs Log Net }
main { Clipboard Clock Db Env Exec Fs Log Net }
run_batch { Clock Db Env Exec Fs Log Net }
…
```
`cargo candor policy` 是 candor 的**架构即代码**层 —— 也是随着模型在局部推理方面变得更好而体现其价值的部分。模型提供建议;只有掌握整个副作用图的工具才能*阻止 PR*。它强制执行 AI Agent 最容易犯的失败模式 —— 在没有看到整个副作用图的情况下编辑一个函数 —— 并且是在任何人都无法在脑海中记住的规模上进行的:一条命令快照工作区中的每个 crate,然后在加载了同级 crate 的情况下进行强制执行,因此边界可以捕获起因存在于*另一个 crate* 中的违规。策略文件声明不变量,candor 标记任何*传递性*的违规:
```
# .candor/policy
deny Net Db Fs domain # the domain layer must reach no I/O — even through a helper
pure parse # parsing must be side-effect-free
deny Exec # nothing may spawn a subprocess
allow Net in billing api.stripe.com # billing may reach the network — but ONLY Stripe
allow Exec in build git # the build layer may run subprocesses — but ONLY git
allow Db in billing ledger.* # billing may touch the database — but ONLY the ledger schema
forbid domain -> infra # the domain layer must not depend on infrastructure
```
```
[AS-EFF-006] `domain::checkout` performs { Db }, forbidden by policy (scope `domain`): `deny Net Db Fs domain`
[AS-EFF-008] `billing::record_activity` reaches { metrics.growthtracker.io:443 } outside the allowlist, forbidden by policy (scope `billing`): `allow Net in billing api.stripe.com`
[AS-EFF-009] `domain::checkout` reaches into a forbidden layer (via `infra::db::save`), violating policy: `forbid domain -> infra`
```
三种边界类型,全部经过*传递性*检查,因此它们能捕获局部 diff 隐藏的内容:
- **`deny` / `pure`** (AS-EFF-006) — 一个层可以做什么。`checkout` 不需要直接接触数据库;candor 会捕获它通过任何被调用者到达 `Db` 的行为。
- **`allow in …`** (AS-EFF-008) — 一个副作用可以触及哪些值:`Net`
主机(“计费层只能与 Stripe 通信”)、`Exec` 命令(“构建层只能运行 git”)、`Fs` 路径
(“配置层只能读取 /etc/app”)。模型无法自我检查的供应链边界,因为字面量隐藏在传递性的、通常是**跨 crate** 的被调用者中(按副作用匹配:主机按名称,命令按基本名称,路径按前缀)。
- **`forbid -> `** (AS-EFF-009) — 一个层可以依赖于谁。领域层不得触及
基础设施,即使是通过一系列辅助函数。
`cargo candor policy` 通过一条命令在整个工作区强制执行所有这三项 —— 它快照每个
crate,然后在加载了同级 crate 的情况下进行强制执行,因此存在于*共享 crate* 中的副作用或端点仍然会在禁止它的边界处被捕获。参见 [examples/candor-policy](examples/candor-policy)
和 [eval/bet3](eval/bet3/RESULTS.md)。
`cargo candor risk` 是一个**咨询性的、启发式的**推动,指向注入类别 —— 参数派生自函数参数的副作用 (`fs::read(format!("/var/cache/{key}"))`, `Command::new(name)`):
```
[AS-EFF-007] `read_user_file` performs { Fs } on caller-derived input (an injection surface — …)
```
它*不是*可靠的污点分析:这是一个语法层面的、过程内的检查,会过度标记或标记不足
(它遗漏了通过结构体字段和跨函数的流,并会标记实际上已验证的参数)。使用它来寻找值得审查的外围接口 —— 切勿将其作为门禁。
### `cargo candor containment` — 不是“分数”的架构信号
原始的副作用*计数*依赖于领域(数据库应用程序有大量的 `Db` —— 这不是缺陷),所以不存在单一的“candor 分数”。但跨层边界副作用的**分散程度**是
与领域无关的:`Db` 都在一个数据层中是架构良好的;`Db` 分散在 `model`,
`actions`, *和* `dao` 中则是存在泄漏的 —— 无论它执行了多少 DB 操作。`containment` 对此进行测量,按
边界副作用 (`Db`/`Net`/`Exec`/`Fs`/`Ipc`);`Log`/`Clock` 是环境性的(被报告,但不评分)。层是从公共 crate 根之后的模块推断出来的,无需配置:
```
effect contained layers owner ← leaked into
Db 55% 3 conn (11) ← query:7, app:2 # pgman: DB is mostly in conn/query…
```
(在 `pgman` 上运行 —— 其 DB *旨在*位于 `conn` + `query` 中 —— candor 独立发现了该边界**及其**一个有记录的例外,即 `app:2` 泄漏。)给定基线前缀,它就是一个**棘轮** —— 对变得更糟进行门禁控制,注意变得更好:
```
[containment] a boundary effect leaked into a layer it wasn't in: ← exit 1, fail the PR
Db → actions
✓ improved — a boundary effect left a layer: ← informational
Db ⊘ legacy
```
使用 `cargo candor containment` 进行诊断,使用 `cargo candor containment .candor/baseline` 作为门禁。
刻意设计为**诊断 + 趋势门禁,而不是单一的评分** —— 绝对水平依赖于领域且可被操纵,但“边界副作用是否泄漏到了新层中?”是一个真实的、可执行的架构质量信号。
## 所有模式(显式调用)
在任何 Rust 项目根目录下,将 `LINT` 设置为 dylib 的绝对路径:
```
# AUDIT(默认):每个函数的 transitive effect set。无需修改代码。
cargo dylint --lib-path "$LINT"
# JSON:机器可读报告,每个 crate+type 一个文件:...json
CANDOR_JSON=/tmp/report cargo dylint --lib-path "$LINT"
# CONFORMANCE:强制执行 inferred ⊆ declared。
CANDOR_STRICT=1 cargo dylint --lib-path "$LINT" # whole crate
CANDOR_STRICT=mymod::sub cargo dylint --lib-path "$LINT" # one module (incremental adoption)
# ENFORCEMENT(cap-std-aligned):标记任何对 ambient authority 的 DIRECT reach。
CANDOR_NO_AMBIENT=mymod cargo dylint --lib-path "$LINT" # AS-EFF-004 per direct ambient call
# REGRESSION GUARD:如果任何函数在已保存的 snapshot 之后获得了 effect,则失败。
CANDOR_JSON=.candor/baseline cargo dylint --lib-path "$LINT" # 1. snapshot (commit it)
CANDOR_BASELINE=.candor/baseline cargo dylint --lib-path "$LINT" # 2. in CI: AS-EFF-005 on regressions
# 可与任何模式结合的 Flags:
CANDOR_CONFIG=candor.rules cargo dylint --lib-path "$LINT" # extra classifier rules
CANDOR_PARANOID=1 cargo dylint --lib-path "$LINT" # treat generic trait dispatch as Unknown
```
或者将其注册到项目的 `Cargo.toml` 中,以便普通的 `cargo dylint` 能找到它 —— 通过本地路径,
或者**直接从 git 获取而无需克隆**(dylint 会根据 candor 固定的工具链获取并构建它)。这相当于 dylint 的依赖项;dylint 只会从 `git` 或 `path` 源加载库,而不会从 crates.io,因此 candor **没有**(也无需)发布在那里。
```
[workspace.metadata.dylint]
# clone-free — 固定一个 tag/rev 以实现可复现性:
libraries = [{ git = "https://github.com/tombaldwin/candor-rust", tag = "v0.3.0" }]
# …或一个本地 checkout:
libraries = [{ path = "/abs/path/to/candor" }]
```
## 它检测什么
candor 回答关于代码库的两个问题:
1. **每个函数执行了哪些副作用?** —— 网络(AWS SDK, `reqwest`/`ureq`/`isahc`, 原始
`std`/`tokio` socket)、数据库 (`sqlx`/`rusqlite`/`postgres`/…)、本地 IPC (Unix socket)、
文件系统、进程生成、环境变量、时钟、随机数、日志、剪贴板 —— 包括通过它调用的函数传递继承的副作用。
2. **签名是否诚实?** —— 一旦你通过模块传递能力 token(或使用 cap-std),它就会标记任何执行了未声明副作用的函数。
它解析每次调用的 `DefId` 并对它落入的 crate/路径进行分类。这种类型解析是关键所在:单纯的 `.send()` 在语法上毫无意义,但解析出的方法告诉我们它属于 `aws_sdk_*` → 一个网络副作用。
## 能力准则(一致性模式)
函数通过将匹配的**能力 token** 作为参数 (`&Fs`, `&Env`, …) 来声明它可以执行的副作用。Token 是不可伪造的 —— 私有字段意味着它们只能被*接收*,而不能在其定义模块之外构造 —— 并且在入口点被一次性铸造。有关模式,请参见 `sample/src/main.rs`。然后检查器会标记:
- **AS-EFF-001** — 函数执行了未声明的副作用。
- **AS-EFF-002** — 函数声明了从未使用的能力。
- **AS-EFF-003** — 函数进行了 candor 无法解析的调用(动态分发、函数指针,或
通过 `impl Fn` 的回调),因此其副作用集合无法被证明是完整的,无法被认证。
- **AS-EFF-004** (`CANDOR_NO_AMBIENT`) — 函数直接获取*环境权限*
(`std::fs`, `std::net`, `std::env`, `std::process`, 时钟, …) 而不是接收
能力。这是与 cap-std 对齐的、*可强制执行*的咨询 token 替代方案:它甚至会在持有 token 的函数上触发,因为持有 `&Fs` 并不能阻止你调用
`std::fs`。解决方法是通过注入的能力(例如 cap-std 句柄)来路由调用。
- **AS-EFF-005** (`CANDOR_BASELINE`) — 现有函数*获得*了在已保存快照中没有的副作用。摩擦最低的采用路径:无需 token 穿线,无需重写 —— 只需捕获导致以前纯函数开始执行网络/磁盘等 I/O 操作的 PR。(新函数不会被标记;它们作为新代码进行审查。)
增量采用:将 `CANDOR_STRICT` / `CANDOR_NO_AMBIENT` 限制在一个模块内,修复直到它报告为零,然后进入下一步。
### 或者使用真正的能力:cap-std
candor 将 [cap-std](https://github.com/bytecodealliance/cap-std) 能力*类型*识别为
声明,并将其操作识别为匹配的副作用。一个接受 `&Dir` 并通过它读取 (`dir.read_to_string(..)`) 的函数是一致的 —— 它声明的 `Fs` 与其推断出的 `Fs` 相匹配 ——
而获取环境 `std::fs` 的同级函数则会被标记。与 candor 自己的咨询 token 不同,
cap-std 能力是不可伪造且在编译时强制执行的;candor 只是在顶层使副作用表面*可见*。参见 `sample-capstd/`。当前的映射:`Dir`→Fs, `Pool`/`TcpStream`→Net,
`SystemClock`→Clock, `UnixStream`→Ipc。
## CI 防护栏(最低摩擦的采用)
你不需要采用能力准则就能获得价值。最廉价的收益是回归防护:快照副作用报告,提交它,并在函数的副作用表面扩大时让 CI 失败。
```
# 一次,在一个已知的正常 commit 上 — 然后 `git add .candor/`
CANDOR_JSON=.candor/baseline cargo dylint --lib-path "$LINT"
# 在 CI 中:仅在 AS-EFF-005(函数获得了 effect)时失败 — 参见 examples/candor-guard.yml
out=$(CANDOR_BASELINE=.candor/baseline cargo dylint --lib-path "$LINT" 2>&1); echo "$out"
echo "$out" | grep -q AS-EFF-005 && { echo "effect surface grew"; exit 1; } || true
```
现在,如果一个 PR 突然让解析器打开 socket,或者让渲染函数开始读取文件系统,就会自动导致审查失败 —— 无需 token,无需重写。当有意添加新的副作用时,刻意地(重新运行快照命令)刷新基线。这对于人工审查者和审查 diff 的 AI Agent 同样有用。
## 它对 Agent 的实际帮助有多大?(诚实的版本)
一项对照试点 ([EVAL.md](EVAL.md)) 让仅使用 JSON 的 Agent 和仅使用源代码的 Agent 在相同的范围界定任务上进行对抗。JSON 大约便宜 3 倍,快约 6.5 倍 —— *并且*它揭示了一个真实的教训:仅使用源代码的 Agent 在某一点上更**准确**,因为 candor 悄悄地将一些
`reqwest` HTTP 调用错误分类了(这是一个分类器漏洞,现已修复)。所以:报告便宜且真正有用,
但**只与其分类器一样正确** —— 这正是存在 `Unknown`/`unresolved` 的原因,也是为什么 Agent 应该将标记为不确定的函数视为“去阅读源代码”,而不是“相信我”的原因。
## 未解析的调用(可靠的稳健性)
candor 无法追溯到具体被调用者的调用 —— `dyn Trait` 分发、函数指针、通过泛型 `impl Fn` 参数到达的闭包 —— 可能执行*任何*副作用。candor 将这些记录为 **`Unknown`** 副作用,而不是默默地假设是纯净的。你将在审计输出和 JSON 的 `unresolved` 标志中看到 `Unknown`;在一致性模式下,它会引发 AS-EFF-003。(*不*这样做的实测代价:在一个真实的约 8k 行代码库中,22% 的函数至少进行了一次无法解析的调用。)
遗留漏洞:静态分发的**泛型** trait 调用(`t.method()`,其中 `t: T: Trait`)假定遵守其边界,而不是标记为 `Unknown` —— 否则每个 `.clone()` /
`.to_string()` / 迭代器适配器都会淹没报告。参见 `CRITIQUE.md`。
## 扩展分类器
`src/lib.rs` 中的 `classify()` 是一个精心策划的表,将 crate/路径映射到副作用。要在不重新构建的情况下识别你
自己有副作用的 crate,将 `CANDOR_CONFIG` 指向一个规则文件 —— 每行一条规则,
` `:
```
# project effect 规则
Net crate reqwest
Fs path mycrate::storage::
```
匹配实际的 I/O 边界,而不是整个 crate —— 例如 SDK 仅匹配 `.send()`,仅匹配 `::process` 的 `Command`/`Child` —— 否则你会过度报告。
## 已知限制
- **动态分发 / 函数指针 / 回调**无法被解析为具体的被调用者。这些被如实地呈现为 `Unknown` (→ AS-EFF-003) 而不是被默默丢弃,但 candor 仍然无法告诉你它们背后隐藏了*哪些*副作用。例外:对于传统上纯净的 std trait (`Display`, `Debug`, `Error`, `ToString`, `Clone`, …) 的 `dyn` 被视为纯函数,而不是 `Unknown` ——
否则像 `dyn Error` 格式化这样的普遍模式会导致报告中充斥着误报。
- **泛型静态分发**(`t: T: Trait` 的 `t.method()`)假定遵守其边界 —— 这是为了保持报告可读性而刻意保留的剩余不可靠性(参见 `CRITIQUE.md`)。
- **咨询性的,而非强制的**:`&Fs` token 实际上并不能限制 `std::fs`;candor 只进行报告。
要进行真正的强制执行,请使用 [cap-std](https://github.com/bytecodealliance/cap-std)。
- **宏生成的常量/静态变量被跳过**(以消除诸如 tracing 的每个日志点的
`__CALLSITE` 静态变量之类的噪音)。宏生成的*函数*(`async_trait` 方法、derive-impl 方法、声明宏 fn)**会**被分析和报告 —— 早期全面跳过确实导致了少报,
此问题已修复,并由模糊测试通道 (`macro_call` / 宏定义的接收器) 进行保障。
- **能力必须是直接参数。** `declared_caps` 仅将能力 (`&Fs`,
cap-std 的 `&Dir`) 识别为顶级参数。通过结构体字段获取的能力(`fn f(ctx: &AppContext)`,其中 `ctx` 持有 `Dir`)不计入已声明 —— 尽管该函数持有能力,在严格模式下它也会被标记。
- **非局部 trait 的泛型静态分发**假定遵守其边界(CHA 只能看透*局部* trait);`CANDOR_PARANOID` 会标记其余部分,但代价是产生噪音。
- 通过宏进行的日志记录在每个函数中去重,但会计算每个记录日志的函数。
## 文档
- **[candor-spec](https://github.com/tombaldwin/candor-spec)** — candor
实现的语言无关规范(副作用词汇、报告 schema、信任契约)。由
[JVM 引擎](https://github.com/tombaldwin/candor-java) 和仅基于规范的
[TS 引擎](https://github.com/tombaldwin/candor-ts) 共享;CI 一致性测试套件使三者保持相同的
答案。
- **[AGENTS.md](AGENTS.md)** — AI Agent 的独立说明(安装 → 运行 → 阅读)。
- **[PRINCIPLES.md](PRINCIPLES.md)** — candor(及其开发)所基于的理念。
- **[CRITIQUE.md](CRITIQUE.md)** — 诚实的、批判性的自我评估 + 与先前技术(Cackle、cap-std、Rust 副作用倡议)的比较。
- **[EVAL.md](EVAL.md)** — 关于报告是否真的能帮助 AI Agent 的对照试点。
- **[BACKLOG.md](BACKLOG.md)** — 已完成的工作、已推迟的工作,以及每个工作的具体原因。
- **[CONTRIBUTING.md](CONTRIBUTING.md)** — 构建/测试,以及如何教导分类器识别新的 crate。
- **[SECURITY.md](SECURITY.md)** — 为什么 candor *不是* 安全边界,以及如何报告漏报(最重要的 Bug 类别)。
## 测试
`cargo test --workspace` 运行 *分类器* 精度规则上的单元测试(例如 `std::net::TcpStream`
是 `Net` 但 `std::net::SocketAddr` 不是)加上加载冒烟测试,以及 `candor-report` /
`candor-query` 工具测试(报告解析/发现,查询命令)。**有状态的核心**(调用图
不动点、CHA、一致性)未进行单元测试 —— 它需要 dylint 测试工具,而该工具不支持 bless —— 因此它由 `sample/`+`sample-capstd/` crate 和一个断言真实审计输出的 CI *行为*检查覆盖(因此“candor 不输出任何内容”的回归会导致 CI 失败)。Lint 还会优雅地失败(绝不会发生 ICE)发生在类型检查主体之外的表达式上。
**可靠性契约** —— “绝不默默地纯净” —— 是其自身的门禁,而不是期望。一个对抗性的
模糊测试器 ([`soundness/`](soundness/)) 生成可编译的 crate,这些 crate 将*已知的*副作用从叶子节点通过随机调用的形式链向上传递,并断言每个可达的函数都报告了该副作用或 `Unknown`(纯净/省略的函数是它寻找的 Bug)。模糊测试通道涵盖了历史上隐藏调用的各种形式 —— 直接/闭包/泛型/装箱回调、`dyn` 和任意 self 分发、UFCS、重载运算符、`?`、`.await`、宏、隐式 `Drop`、不透明的 `impl Trait` 返回值,以及跨 crate 边界 —— 每一个都经过了*有效性验证*(恢复修复会导致通道失败)。另外两个
通道在 `strace` 下运行每个程序,并确认 candor 的静态预测过近似了
内核实际观察到的副作用 —— 这种真实情况不信任任何关于测试是如何生成的假设。
## 状态
原型。在一个真实的约 8k 行代码库(`ebman` AWS Elastic Beanstalk TUI)上进行了验证:
审计标记了约 445 个函数;一个叶子模块被转换为能力准则,并在继续基于稳定版构建的同时达到了零一致性违规。
candor 也**保护自己**:CI 针对 `.candor/baseline` 在 candor 上运行 candor。其有副作用的表面 —— lint 中的五个函数(配置 / 基线 / 跨报告读取 + 报告写入,全部为 `Env`/`Fs`),加上 `candor-report` 的 `report_files` (`Fs`) 和构建脚本 (`Exec`/`Fs`) —— 无法在不知不觉中获得*新*的副作用。请注意防护的范围,老实说:根据 AS-EFF-005 的设计,它标记的是*现有函数中的回归*,而不是全新的函数(那些作为新代码进行审查),因此新添加的有副作用的函数不会触发它。当有意引入新的副作用时,使用 `cargo candor snapshot .candor/baseline` 刷新。
## 许可证
根据您的选择,采用 [MIT](LICENSE-MIT) 或 [Apache-2.0](LICENSE-APACHE) 双重许可。
标签:Rust, 代码质量检查, 可视化界面, 架构边界, 网络流量审计, 通知系统, 错误基检测, 静态代码分析