fix(gate): 测试不对称信号认得本仓自己的 tests.rs 约定——96 百分位假阻断 - #170
Conversation
`is_test_file` 的三条判据全都漏掉本 crate 自己的内联测试模块约定 ——`change_risk/tests.rs`、`change_agenda/tests.rs`、`file_gate/tests.rs`: 最后一段是 `tests.rs` 不是 `tests` 目录,文件名不以 `test_` 开头, 词干 `tests` 不以 `_test` 结尾。 后果不是理论上的。PR #166 加了 229 行 `file_gate/tests.rs`,门禁报 「source changed, no tests touched」并以 96 百分位挡下它。测试不对称是 公式里权重最大的单项(WEIGHT_TEST_ASYMMETRY = 25)。 同一份 diff,修前修后: 修前: score 86 percentile 96 level high asymmetric=true 修后: score 61 percentile 53 level low asymmetric=false test=1 而且这是本仓两条规则互相打架:巨石规则(loc>800 或 fn>25&&loc>400) 把测试模块推进独立文件,这个信号又拒绝承认它们。本轮三个 agent 按约定 拆分之后全都会踩。 判据用整词干相等,不是子串或后缀匹配——`latest.rs`、`contests.rs`、 `testsuite_helpers.rs` 是普通源文件,不能被算成测试,有用例守着。 Refs #152 #95 #165
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
🔒 Repowise is not analyzing this repository The PR bot is free on public repositories. This one is private, which needs a Pro plan. |
|
Warning Review limit reached
Next review available in: 40 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummary by CodeRabbit
Walkthrough
ChangesTest file detection
Estimated code review effort: 2 (Simple) | ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Intel change risk
Top signals
revspec: |
这个 PR 在自己身上验证了自己本 PR 只碰两个文件: 没有这个修复的话, 同一条判据,同一次运行:修复让它认出了修复自己带的测试。 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/code-intel-cli/src/change_risk/tests.rs`:
- Around line 116-128: Add an asymmetric-signal contract test alongside
is_test_file_credits_this_crates_own_inline_test_module_convention that
evaluates a change containing both a source file and
crates/code-intel-cli/src/change_risk/tests.rs. Assert
testAsymmetry.testFilesChanged is greater than zero, asymmetric is false, and
subscore is zero; if the reported files output includes entries, also assert the
tests.rs entry has isTestFile set to true.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6a1e4a21-171f-4566-8e64-2e775cbaa91a
📒 Files selected for processing (2)
crates/code-intel-cli/src/change_risk/signals.rscrates/code-intel-cli/src/change_risk/tests.rs
CodeRabbit 在 #170 上指出:只测 `is_test_file` 不够。缺陷本身活在 `testAsymmetry` 报出来的东西里,判据绿了不等于信号对;将来有人重写 接线、判据仍正确但不再被查,现有用例一条都不会红。 补两条走 `score_subset` 出口的契约测试,断言的是发出去的 JSON 字段, 不是内部结构体: - 源文件 + `change_risk/tests.rs` 的变更:`testFilesChanged == 1`、 `asymmetric == false`、`subscore == 0`,且 `files` 里 tests.rs 那行 `isTestFile == true`。 - 互补的另一半:只碰源文件的变更仍然 `asymmetric == true`、 `subscore == 1`。少了这条,一个「让所有变更都显得对称」的错误修法 会通过上面那条,并静默解除公式里权重最大的那一项。 Refs #152 #95
* fix(gate): god-file ratchet compares identities, not counts — new god files can no longer ship green Three real branches each added a god file while every authoritative self-scan stayed green (#165): the ratchet compared god_file_count against a stale, never-tightened baseline, and the slack swallowed the regressions. A fix+regress swap kept the count flat entirely. - .sentrux/baseline.json schema v5: records every tolerated god file by path with measured loc/functions and the rule branch it trips; a baseline without the identity list fails closed as baseline_engine_mismatch - god_files_increased now fires on any god file the baseline does not list, naming the file, the branch (loc>800 or functions>25&&loc>400), and the measured values — files, not counts (#148 C1) - green gates report reclaimable slack when baseline entries are no longer over threshold; the gate never rewrites repository state - thresholds extracted to GOD_FILE_* consts so measurement and reporting share one source - decision recorded in .sentrux/rules.toml: test functions count (option B), the cfg-aware alternative explicitly rebutted; the tests.rs convention is the structural answer to #155's case - repo baseline re-saved as v5 (33 standing god files grandfathered by identity); repin --write resynced sentrux_gate.rs digests Refs #165 #148 #106 #95 * style: rustfmt + repin after formatting cargo fmt re-wrapped one assertion; formatting changes bytes, so the sentrux_gate.rs digest pins were resynced with repin --write and the digest suites re-run (48 + 91 green). Refs #165 * test(gate): binary-level coverage for the v5 identity ratchet The sentrux_gate.rs unit tests pin run_gate directly; these three prove the same contract through the shipped CLI surface the authoritative self-scan and CI actually invoke: save_baseline writes the v5 godFiles identity list, check fails naming a new god file with its rule branch and measured values, and grandfathered god files stay green with the reclaimable-slack advisory after a fix. Refs #165 * style: rustfmt on sentrux_gate_cli.rs Local fmt --check was piped through tail, which swallowed its exit code and reported clean on a dirty file. Verified unpiped this time. * chore(gate): re-save v5 baseline against the rebased tree Same 33 god files by identity on the post-#149/#159/#170/#162 main; gate and check verify green. * fix(internalization): repin sentrux_gate.rs digests after final rebase Conflict resolution took main's pin values, which predate this branch's sentrux_gate.rs changes — the committed-stale form repin cannot see. Replaced both operationTrace pins with the live hash; repin clean, four suites green. Baseline v5 re-saved on the final tree (same 33 identities). CodeRabbit wording fix from #171 review folded into the #168 changelog entry (code-intel run execute, generic validator). Refs #165
`change_risk/scoring.rs` 是 37 行算术,决定每张 PR 被不被拦,此前没有任何直接单测。所有经过它的测试走端到端路径,断言的是产物形状(字段在不在、schema 合不合法、数组空不空),不是数值。
变异测试(`cargo mutants --file .../scoring.rs -- --bin code-intel`,判据为 544 个单测)生成 31 个可编译变异体,26 个存活,包括:
replace combine -> f64 with 0.0
replace compute_percentile -> u32 with 0
replace level_for_percentile -> &'static str with ""
外加 combine 里 30/25/25/20 权重结构的每一个 + / * 互换。门禁对这些全部保持绿灯。这正是 #170 那次 96 百分位假阻断没被任何测试拦住的原因:真因在 signals.rs,而下游 scoring.rs 把荒谬输入照单全收算成 high,整条数值链从未被断言过。
本提交加 8 条测试,每条断言一个具体数字:
- combine 的四信号加权和钉死在 42.5
- 每个信号单独贡献钉死在 15 / 6.25 / 18.75 / 2.5,锁住 30/25/25/20
- 满信号恰为 100、零信号恰为 0,兑现模块文档「0-100 分」的声明
- compute_percentile 用 3/5 非对称样本钉死在 60,使比较方向可测
- <= 而非 <:与样本齐平算第 100 百分位
- 空 baseline 返回 0(文档化的 fail-open)
- level 阈值 90 / 60 两侧各钉一次
- round2 的半分位舍入
subscore 取值刻意选成互异、非零、非 1.0 且二进制可精确表示(1/2、1/4、3/4、1/8):非 1.0 是因为 w * 1.0 与 w / 1.0 同值,互异是为了任一算符替换都落在不同总和上,精确可表示是为了用 == 比较而不是 epsilon——epsilon 会重新放出这批测试正要收紧的余量。
## 验证
重跑同一条变异命令、同一判据:35 个变异体全部被杀,missed.txt 为空。26 个存活体一个不剩。
测试放在 scoring.rs 内联 mod tests,不进 change_risk/tests.rs:后者已是 25 函数 / 385 loc,追加会越过 god file 判据 functions > 25 && loc > 400(#165 的坑)。scoring.rs 现为 114 loc / 14 函数,两条阈值都远未触及。
顺带把 mutants.out/ 加进 .gitignore:不忽略它会扰动本仓自己的度量,实测 sentrux scan 的 file-gate 候选数在跑变异测试期间从 32769 变成 32839。
## 门禁
- cargo fmt --check:clean
- cargo test -p code-intel:3432 passed / 0 failed
- repin:clean
- 反证:对旧实现跑变异测试 26 个存活,对新实现跑 0 个存活
Refs #179 #170 #165 #55
版本位五处同步(Cargo.toml、Cargo.lock、cli-head-parity 夹具的 old+new 两份 stdout、 toolchain-versions.v1.json 的 declaredIn 绑定),和 v0.7.0-beta.5 (#164) 完全同一组文件。 CHANGELOG 补上一批漏账:beta.5 之后的 13 个 commit 里只有 4 个进过 `[Unreleased]`。 本次补写 6 条正文条目(锚点验证闸 #151、扫描面具名排除闸门 #152、文档语言首选项 #155、 测试不对称信号认 tests.rs 约定 #170、sentrux shim lite 门与权威 baseline 分居 #182、 测试临时目录按进程归属 #175)与 3 条 Notes(#179 变异测试补测、#189 agent-approved label 门、#156 OpenSpec 记录核实),`[Unreleased]` 收敛为 `[0.7.0-beta.6] — 2026-08-05`。 漏账本身是 #174 想解的那条单行道税的现症,不是本次新引入的。 发布前证据(本机,非 CI): - `cargo test -p code-intel --locked`:3432 passed / 0 failed,55 个测试二进制。 首轮 `-j` 默认并行撞本机 `rustc-LLVM ERROR: out of memory`(宿主 commit 贴顶, 与 #123 bug 1 同因),`-j 2` 复跑全绿——是环境限制不是代码红灯。 - `cli_head_parity` 3/3、`toolchain_versions` 5/5:版本绑定判据全绿。 - `tests/test_repository_layout.py` 5/5、`tests/test_skill_package.py` 18/18。 - 权威 self-scan(release 构建,`run execute --doctor-require-repowise false`): exitCode 0,9 个节点全 pass,anchors 4596 verified / 0 approximate / 0 dropped, `failures.process` 与 `failures.domain` 皆空。 不含任何行为变更:本次 diff 只有版本字符串与 CHANGELOG。
PR 门禁在 #166 上报了假信号,并据此以 96 百分位把它挡下。这个 PR 修判据本身。
现象
#166 新增了
crates/code-intel-cli/src/file_gate/tests.rs,229 行测试。pr-gate.yml的报告:原因
change_risk/signals.rs的is_test_file三条判据,对本 crate 自己的内联测试模块一条都不命中:crates/code-intel-cli/src/file_gate/tests.rstests目录段tests.rs,不是teststest_开头_test结尾tests而
is_source_file命中了(crates/开头 + 含src)。那 229 行测试既不算测试,还被计入源码改动。这不是一次性巧合。本 crate 的多段模块一律「模块目录 + 同级
tests.rs」——change_risk/、change_agenda/,以及本轮新增的file_gate/、language_pref/、graph/、anchor_verification/。凡遵守约定的 PR 都会被判成「没碰测试」,而测试不对称是公式里权重最大的单项(WEIGHT_TEST_ASYMMETRY = 25)。更难受的是,这是本仓两条规则互相打架:巨石规则(
loc > 800或functions > 25 && loc > 400)把测试模块推进独立文件,这个信号又拒绝承认它们。本轮三个 agent 按约定拆分之后,全都会踩。同一份 diff,修前修后
25 分的摆幅,正好是
WEIGHT_TEST_ASYMMETRY。96 百分位(阻断)变 53 百分位(放行)。改动
is_test_file增加第四条判据:词干整词等于tests。整词干相等,不是子串或后缀匹配——
latest.rs、contests.rs、testsuite_helpers.rs是普通源文件,不能被算成测试。有专门的负例用例守着。is_source_file未改:内联测试模块确实在src/下、确实参与编译,把它算成源文件是对的;asymmetric只要求test_files_changed > 0,所以一个文件同时算两者不影响判定。门禁
cargo fmt --checkcargo test -p code-intel --bin code-intel落地顺序
#166 自己的分支没有这个修复,CI 用的是从 PR 分支构建的二进制,所以它会一直红到本 PR 合入 main 且 #166 rebase 之后。建议先合本 PR。 不建议给 #166 贴
risk-accepted——那是拿标签盖住一个真实的门禁缺陷,而这个缺陷会继续影响之后每一个 PR。Refs #152 #95 #165