Skip to content

fix(gate): 测试不对称信号认得本仓自己的 tests.rs 约定——96 百分位假阻断 - #170

Merged
2233admin merged 2 commits into
mainfrom
fix/test-asymmetry-recognises-tests-rs
Aug 4, 2026
Merged

fix(gate): 测试不对称信号认得本仓自己的 tests.rs 约定——96 百分位假阻断#170
2233admin merged 2 commits into
mainfrom
fix/test-asymmetry-recognises-tests-rs

Conversation

@2233admin

Copy link
Copy Markdown
Owner

PR 门禁在 #166 上报了假信号,并据此以 96 百分位把它挡下。这个 PR 修判据本身。

现象

#166 新增了 crates/code-intel-cli/src/file_gate/tests.rs229 行测试pr-gate.yml 的报告:

Score 86/100 · Percentile 96th · Level 🔴 high
Test asymmetry: source changed, no tests touched

原因

change_risk/signals.rsis_test_file 三条判据,对本 crate 自己的内联测试模块一条都不命中

判据 crates/code-intel-cli/src/file_gate/tests.rs
路径含 tests 目录段 最后一段是 tests.rs,不是 tests
文件名以 test_ 开头
词干以 _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 > 800functions > 25 && loc > 400)把测试模块推进独立文件,这个信号又拒绝承认它们。本轮三个 agent 按约定拆分之后,全都会踩。

同一份 diff,修前修后

修前:  score 86   percentile 96   level high   asymmetric=true
修后:  score 61   percentile 53   level low    asymmetric=false   test=1

25 分的摆幅,正好是 WEIGHT_TEST_ASYMMETRY。96 百分位(阻断)变 53 百分位(放行)。

改动

is_test_file 增加第四条判据:词干整词等于 tests

整词干相等,不是子串或后缀匹配——latest.rscontests.rstestsuite_helpers.rs 是普通源文件,不能被算成测试。有专门的负例用例守着。

is_source_file 未改:内联测试模块确实在 src/ 下、确实参与编译,把它算成源文件是对的;asymmetric 只要求 test_files_changed > 0,所以一个文件同时算两者不影响判定。

门禁

结果
cargo fmt --check clean
cargo test -p code-intel --bin code-intel 433 passed

落地顺序

#166 自己的分支没有这个修复,CI 用的是从 PR 分支构建的二进制,所以它会一直红到本 PR 合入 main 且 #166 rebase 之后。建议先合本 PR。 不建议给 #166risk-accepted——那是拿标签盖住一个真实的门禁缺陷,而这个缺陷会继续影响之后每一个 PR。

Refs #152 #95 #165

`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
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@repowise-bot

repowise-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

🔒 Repowise is not analyzing this repository

The PR bot is free on public repositories. This one is private, which needs a Pro plan.

See plans · Manage this repository

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@2233admin, you've reached your PR review limit, so we couldn't start this review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e09a5dd-ba7b-406d-b821-d98b311c74ba

📥 Commits

Reviewing files that changed from the base of the PR and between 7ec9665 and 79d3b14.

📒 Files selected for processing (1)
  • crates/code-intel-cli/src/change_risk/tests.rs
📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved recognition of test files named tests, including inline module files such as tests.rs.
    • Prevented unrelated filenames that merely contain tests from being classified as test files.
  • Tests

    • Added coverage for valid inline test modules and similar-looking filenames.

Walkthrough

is_test_file now recognizes exact tests stems, including tests.rs, while preserving existing patterns. Tests cover inline test modules and reject filenames that only contain tests.

Changes

Test file detection

Layer / File(s) Summary
Test file detection and validation
crates/code-intel-cli/src/change_risk/signals.rs, crates/code-intel-cli/src/change_risk/tests.rs
is_test_file recognizes exact tests stems and documents all supported patterns. Tests cover tests.rs and reject unrelated filenames containing tests.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a rabbit, hopping through the tests,
tests.rs now joins the nest.
latest.rs stays source and clear,
contests.rs does not appear.
Exact names guide every leap!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the fix for test asymmetry detection of the repository's tests.rs convention and the resulting false block.
Description check ✅ Passed The description directly explains the false signal, the tests.rs detection fix, the added safeguards, and the validation results.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Code Intel change risk

Score Percentile Level
21/100 10th (vs last 48 commits) 🟢 low

Top signals

  • Diff shape: 2 file(s), +98/-4 (max file share 0.77)
  • Test asymmetry: source changed, tests touched
  • Bug-magnet: 1 fix commit(s) in touched files (180d)
  • Churn: 6 commit(s) touching these files (90d)

revspec: origin/main..HEAD · threshold: percentile >= 90 blocks unless labeled risk-accepted · code-intel change risk

@2233admin

Copy link
Copy Markdown
Owner Author

这个 PR 在自己身上验证了自己

本 PR 只碰两个文件:change_risk/signals.rschange_risk/tests.rs。门禁跑的是从本分支构建的二进制(已含修复),报告:

Score 17/100 · Percentile 10th · Level 🟢 low
Test asymmetry: source changed, tests touched

没有这个修复的话,change_risk/tests.rs 对判据完全不可见——最后一段是 tests.rs 不是 tests 目录段,不以 test_ 开头,词干 tests 不以 _test 结尾——门禁会报「no tests touched」,正是 #166 挨的那一刀。

同一条判据,同一次运行:修复让它认出了修复自己带的测试。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 868c289 and 7ec9665.

📒 Files selected for processing (2)
  • crates/code-intel-cli/src/change_risk/signals.rs
  • crates/code-intel-cli/src/change_risk/tests.rs

Comment thread crates/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
@2233admin
2233admin merged commit a3ac9a1 into main Aug 4, 2026
8 checks passed
2233admin added a commit that referenced this pull request Aug 4, 2026
Same 33 god files by identity on the post-#149/#159/#170/#162 main;
gate and check verify green.
2233admin added a commit that referenced this pull request Aug 4, 2026
Same 33 god files by identity on the post-#149/#159/#170/#162 main;
gate and check verify green.
2233admin added a commit that referenced this pull request Aug 4, 2026
* 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
2233admin added a commit that referenced this pull request Aug 4, 2026
`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
@2233admin 2233admin mentioned this pull request Aug 5, 2026
2233admin added a commit that referenced this pull request Aug 5, 2026
版本位五处同步(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。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant