feat(gate): G1 -- computed honesty bit for repin's scan coverage - #141
Conversation
Add EvidenceOutcome (complete/partial/not-computed), a completeness claim for an evidence-producing surface that cannot be asserted without backing data: Complete can only be constructed by supplying the EvidenceScope it covers (artifact types consumed, paths scanned, excluded prefixes), and deserializing a claim whose scope is missing, incomplete, or carries leftover fields from a different status is a parse error rather than a silently-defaulted value. Apply it to repin's report end to end: RepinReport now stores one EvidenceOutcome, and both the JSON scanCoverage field and the clean/exit-code verdict (is_clean/has_unresolved) read that same value, so they cannot drift apart. This closes a real instance of the #133 failure shape found while choosing a surface: excluding every tracked file out of the scan (or pointing --repo at a tree with none) previously reported "clean": true, "filesChanged": 0 -- a vacuous scan indistinguishable from a genuine pass. It is now reported not-computed and fails the gate. Reverse test (the acceptance condition, not a nice-to-have): forging a real not-computed/partial report's status to "complete" is rejected by from_json. The realistic case -- flipping only `status` on a genuine Partial, leaving its well-formed scope and partialReason/paths intact -- is caught by an exact-key-set check per status, since a bare "does scope exist" check would accept it. Verified empirically both ways: temporarily disabling the exact-key check reproduces the forged report as an accepted Complete value, confirming the test is not vacuous. Updates the CLI head-parity fixture: repin's report gained a new field, so its golden case moves from the exact-parity list to intentionalDeltas (same mechanism already used for the help-v2 text change), and the parity test generalizes from "there is exactly one delta" to enumerating the documented set. Refs #139 #138 #133
|
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: 31 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 (6)
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: |
run execute 发布前新增一道锚点验证闸:文件锚点(路径是否还在)、行区间锚点、 符号锚点(声称的名字能否在声称的 file:startLine 重新解析出来)——只在声称的 那一个文件内找,不做全仓搜,解不出就是解不出。 三态而非两态:verified / approximate(同名符号还在同一文件、只是行漂了,带 纠正后的行号) / dropped(整个文件都不在了,或者这个文件里再也找不到这个名字, 带原因)。状态类型仿 G1 的 EvidenceOutcome(#141)——Approximate 造不出来除非真的 带着纠正行号,Dropped 造不出来除非真的带着 reason;from_json 对每个状态做 穷尽 key 校验,把“只改 state 字段、留下另一状态残留字段”这种伪造挡在反序列化 边界上(anchor_verification.rs 的伪造测试直接照抄 G1 的伪造测试)。 计数 {verified, approximate, dropped} 出现在两个地方,缺一不可:新产物 verification.anchors/anchor-report.json 自己的 counts(artifact_ref.rs 的校验器 会把 counts 与产物里实际的 state 逐条核对,数字对不上直接拒收),以及 run execute 顶层 CLI 输出的 anchors 字段(execution-result.v1 schema 已同步加上 这个必填字段)——dropped>0 不需要打开 manifest 或者这个产物本身就能看到。 范围取舍:没有把 dropped 的锚点从 ranking.json / surgery-plan.json 原地摘除。 这两个产物已经进了内容寻址链条(run-manifest.json 的 sha256、 run-manifest-ref.json、以及 artifact_ref.rs 里好几个闭合 key 的校验器,其中 surgery_plan 还嵌套在 hospital-report.json 里单独校验一遍),原地改字段需要 重算哈希、逐层传播、加宽每一个碰到这块形状的校验器。改成新增一个从不曾被 哈希过的旁路产物(仿 repository.iteration/verification.session-evidence 的 “DAG 跑完后再插一个节点、重新落盘 manifest”套路),三态划分和“剔除”发生在 这个新产物内部,原产物字节不动。issue 原文“dropped 锚点被剔除”按字面读也可以 指原产物本身——这里选了旁路产物,是权衡后的范围决定,不是漏掉。 实际验证的锚点:ranking.json 的 ranked files(文件锚点)、code_evidence.symbols 的 name@file:startLine(符号锚点,行区间隐含在符号重新解析里)、 surgery-plan.json 的 primary_target.file(文件锚点,当前 name/source_anchor 恒为 null,无符号可查)。change_impact 是独立 CLI 命令,只读查询,没有自己的 commit/发布步骤,已经有自己的粗粒度整仓快照新鲜度闸(change_impact.rs 里的 evidence.freshness),接入需要不同的挂载点,留作后续。audit_report/ 已排查: 它的 Applicability.surface_evidence 是自由文本证据说明,不是 path/line/symbol 形状的位置声称,这一版没有可供本闸验证的锚点形状。 native_code_evidence.rs 只做了三处最小改动(#152 也碰这片扫描面,按要求保持 克制并在此说明):safe_join/lines 两个纯可见性提升(private -> pub(crate), 零行为变化),外加新增一个 find_symbol_line(复用已有的 symbol_candidate 啟发式, 不第二次实现同一套规则)。 门禁:cargo fmt --check 干净;cargo test -p code-intel 2996 passed / 52 suites;native_code_evidence.rs 是 pinned 文件,repin --write 后 report 模式 确认 clean/filesChanged:0(同时把仓库里另外几处与本次改动无关的既有 stale digest 一并刷新,未手工挑拣);本仓一次真实 run execute 自扫描 exitCode=0、 outcome=completed、publication.status=committed, anchors={"verified":4436,"approximate":0,"dropped":0}(两个真实来源: ranking.json 与 symbols.json;surgery-plan.json 本轮 diagnosis 是 clean snapshot 无 target,按设计不产生条目)。 Refs #151 #141
…#167) * feat(precision): 锚点验证闸——三态 verified/approximate/dropped + 计数出账 (#151) run execute 发布前新增一道锚点验证闸:文件锚点(路径是否还在)、行区间锚点、 符号锚点(声称的名字能否在声称的 file:startLine 重新解析出来)——只在声称的 那一个文件内找,不做全仓搜,解不出就是解不出。 三态而非两态:verified / approximate(同名符号还在同一文件、只是行漂了,带 纠正后的行号) / dropped(整个文件都不在了,或者这个文件里再也找不到这个名字, 带原因)。状态类型仿 G1 的 EvidenceOutcome(#141)——Approximate 造不出来除非真的 带着纠正行号,Dropped 造不出来除非真的带着 reason;from_json 对每个状态做 穷尽 key 校验,把“只改 state 字段、留下另一状态残留字段”这种伪造挡在反序列化 边界上(anchor_verification.rs 的伪造测试直接照抄 G1 的伪造测试)。 计数 {verified, approximate, dropped} 出现在两个地方,缺一不可:新产物 verification.anchors/anchor-report.json 自己的 counts(artifact_ref.rs 的校验器 会把 counts 与产物里实际的 state 逐条核对,数字对不上直接拒收),以及 run execute 顶层 CLI 输出的 anchors 字段(execution-result.v1 schema 已同步加上 这个必填字段)——dropped>0 不需要打开 manifest 或者这个产物本身就能看到。 范围取舍:没有把 dropped 的锚点从 ranking.json / surgery-plan.json 原地摘除。 这两个产物已经进了内容寻址链条(run-manifest.json 的 sha256、 run-manifest-ref.json、以及 artifact_ref.rs 里好几个闭合 key 的校验器,其中 surgery_plan 还嵌套在 hospital-report.json 里单独校验一遍),原地改字段需要 重算哈希、逐层传播、加宽每一个碰到这块形状的校验器。改成新增一个从不曾被 哈希过的旁路产物(仿 repository.iteration/verification.session-evidence 的 “DAG 跑完后再插一个节点、重新落盘 manifest”套路),三态划分和“剔除”发生在 这个新产物内部,原产物字节不动。issue 原文“dropped 锚点被剔除”按字面读也可以 指原产物本身——这里选了旁路产物,是权衡后的范围决定,不是漏掉。 实际验证的锚点:ranking.json 的 ranked files(文件锚点)、code_evidence.symbols 的 name@file:startLine(符号锚点,行区间隐含在符号重新解析里)、 surgery-plan.json 的 primary_target.file(文件锚点,当前 name/source_anchor 恒为 null,无符号可查)。change_impact 是独立 CLI 命令,只读查询,没有自己的 commit/发布步骤,已经有自己的粗粒度整仓快照新鲜度闸(change_impact.rs 里的 evidence.freshness),接入需要不同的挂载点,留作后续。audit_report/ 已排查: 它的 Applicability.surface_evidence 是自由文本证据说明,不是 path/line/symbol 形状的位置声称,这一版没有可供本闸验证的锚点形状。 native_code_evidence.rs 只做了三处最小改动(#152 也碰这片扫描面,按要求保持 克制并在此说明):safe_join/lines 两个纯可见性提升(private -> pub(crate), 零行为变化),外加新增一个 find_symbol_line(复用已有的 symbol_candidate 啟发式, 不第二次实现同一套规则)。 门禁:cargo fmt --check 干净;cargo test -p code-intel 2996 passed / 52 suites;native_code_evidence.rs 是 pinned 文件,repin --write 后 report 模式 确认 clean/filesChanged:0(同时把仓库里另外几处与本次改动无关的既有 stale digest 一并刷新,未手工挑拣);本仓一次真实 run execute 自扫描 exitCode=0、 outcome=completed、publication.status=committed, anchors={"verified":4436,"approximate":0,"dropped":0}(两个真实来源: ranking.json 与 symbols.json;surgery-plan.json 本轮 diagnosis 是 clean snapshot 无 target,按设计不产生条目)。 Refs #151 #141 * docs(adr): 补上锚点验证闸的 precision-over-recall 决策记录 (#151) 上一提交(84313b3)实现了闸门本身,但把取舍理由只写进了 anchor_verification.rs 的模块文档注释里——issue 原文明确要求"在 docs/ 下 显式声明 precision-over-recall 立场,对标 OCR 的公开取舍说辞",代码注释 不算数,之前漏掉了这一步。 补上 docs/adr/0014-anchor-verification-precision-over-recall.md:三态设计 仍然照抄 G1 的 EvidenceOutcome 纪律,取舍论证具体对了一下 alibaba/open-code-review 自己公开的说法——它也接受更低的 recall 换更高的 precision,原话是"a deliberate trade-off favoring precision over noise", 关键步骤靠工程逻辑而非模型兜底。本闸对锚点做的是同一件事:宁可剔除一个 解不出的位置声称并计数,也不让它带着错误位置发布出去。 门禁:cargo fmt --check 干净;cargo test -p code-intel 2996 passed / 52 suites(新增的是纯 Markdown,不受影响)。 Refs #151 * fix(precision): 拆 anchor_verification 避免撞 god-file 门槛 (#151) sentrux_gate.rs:794 的判据是 loc>800 || (functions>25 && loc>400)。单文件版 anchor_verification.rs 是 775 loc / 33 fn,第一个条件没撞线,第二个撞了 (33>25 且 775>400),把本仓 god_file_count 从基线的 32 推到 33——本仓 run execute 自扫描当时是绿的,只是因为 no_god_files 在这仓的规则里本来就是 false、ratchet 比的又是那份宽松的 .sentrux/baseline.json,自扫描对这条规则 不构成真实门禁。 按 change_risk/change_agenda 现成的目录约定拆开:mod.rs + tests.rs, mod.rs 里换成 `#[cfg(test)] mod tests;` 声明。functions 计数器按物理行 前缀识别(`is_function_line`,认 "fn "/"pub(crate) fn " 等前缀开头的行), so 挪走的是测试模块自己的 18 个 fn,mod.rs 剩 15 个、loc 掉到 400 以内, 两个条件都不再触发;没有为了压数字而在没有第二条关注点的地方硬拆—— 状态/计数类型和文件/符号解析器仍然是同一个概念,留在一个 mod.rs 里。 sentrux scan . 验证:拆分前 285 files / god_file_count 33(此分支相对基线 +1 god file 就是 anchor_verification.rs 自己);拆分后 285 files / god_file_count 32,回到基线数字。 门禁:cargo build -p code-intel 干净;cargo test -p code-intel 里 anchor_verification 的 17 个测试原样全过(纯物理挪动,逻辑一行没动)。 Refs #151 * fix(registry): recompute toolchain digests clobbered by rebase conflict resolution Taking main's integrations.json dropped this branch's digest updates for native_code_evidence.rs and artifact_ref.rs (committed-stale form, invisible to repin). Recomputed from the declared input paths; the full bin test suite passes. Refs #151 * fix(e07): regenerate native-code retirement packet after anchor wiring This branch edits native_code_evidence.rs, which E07 freezes; the packet went stale on windows-build-test-package. Regenerated with the original EvaluatedAt 1785748660; retirement suite 8+2 green. Refs #151
What
Gate G1 of the project charter (#139, tracked in #138): the honesty bit is
computed, not asserted.
Adds
EvidenceOutcome(crates/code-intel-cli/src/evidence_outcome.rs) — acompleteness claim for one evidence-producing surface, with three states:
Complete(EvidenceScope)— the surface was fully enumerated. The onlyconstructor for this variant takes an
EvidenceScope(artifact typesconsumed, paths scanned, excluded prefixes); there is no way to build a
Completein Rust without one.Partial { reason: PartialReason, scope: EvidenceScope }— reason isTruncated,CapHit, orUnreadableInput(paths); scope says what wascovered despite the gap.
NotComputed { reason: String }— the surface was never evaluated, and why.Applied end to end to one real surface:
repin's scan-coverage claim.Why repin, not the risk/context truncation surfaces
I read
evidence_query.rs,change_risk/,hospital_diagnosis.rs, andrepin.rsbefore choosing. The risk/context "callers/co-change/tests-to-run"truncation pattern described in #138's body doesn't exist as code in this
repo today — I grepped for
callers_total,tests_to_run,cochange, etc.and found no matches; that description characterizes a different product's
existing shape, not something already broken here.
change_risk's caps(
FILES_TOUCHED_CAP,LINES_CHANGED_CAP) saturate a score, not a coverageclaim — there's no "clean"-style pass/fail bit being asserted there today.
repinis where the silent-lie failure mode is real and already reported(#133):
repin --write --jsonprinted"clean": true, "filesChanged": 0while digests it never scanned were stale. Reading
repin.rsend to end, Ifound and reproduced a second, cheaper instance of the same shape today,
independent of #133's own root cause (which is about digest tracking across
commits, not about scan enumeration — a separate, larger fix):
A scan that covered zero files reported exactly the same
clean: trueshape as a scan that covered everything and found it clean — the report
gives a downstream reader no way to tell "checked, and it's fine" from
"never checked." That's the concrete proof-of-need for this surface.
repin.rswas also not the concurrently-edited file (native_code_evidence.rschunk-emission work is untouched by this PR — confirmed via
git diff --statbefore push).
What changed in repin
RepinReportnow stores oneEvidenceOutcome, computed once inscan_coverage()from the exact samescan_targets/skippeddata everyother field on the report is built from:
scan_targetsafter exclusions →NotComputed(the bug above).gates_cleanskip (too-large / unreadable input) →PartialwithUnreadableInputand the paths that couldn't be read.Complete, with the scope (artifact_types: ["tracked-text-file"],the scanned paths, the exclude prefixes).
Both the JSON
scanCoveragefield andis_clean()/has_unresolved()(whichdrives the exit code) read this one stored value — they cannot read
different data, which is the specific anti-pattern the charter calls out
(an interval computed in one structure, a pass/fail decision read from
another, and the two never meeting).
The reverse test — proof it actually fails when forged
forged_complete_claim_without_scope_is_rejectedincrates/code-intel-cli/tests/repin.rs, plus unit tests inevidence_outcome.rs. Two forgeries, not one:not-computedreport (from the empty-scan-surface case above),statusflipped to"complete"with noscopeadded at all.partialreport (oversized-input case),statusflipped to"complete"— leaving its genuine, fully-populatedscopeand itspartialReason/pathsfields completely untouched. Thisis the shape bug(repin): operationTrace 与 evidenceIds 内嵌 digest 不在扫描面——clean 报告失真 #133's own bug took: one field changed, everything else
left alone. A bare "does
scopeexist and have its fields?" check wouldaccept this forgery, because the scope really is there and really is
well-formed (just borrowed from a claim that never achieved completeness).
from_jsoncatches it with an exact-key-set check per status — aPartial's object is a strict superset of aComplete's, so the leftoverpartialReason/detail/pathsfields fail the check.I verified this test is not vacuous by breaking the fix and confirming the
test fails to fail: with the exact-key check temporarily removed,
EvidenceOutcome::from_jsonon the forged-from-partial JSON returnedOk(Complete(...))instead ofErr(...), and both the integration test andthe unit test failed as expected. Re-added the check, both pass again.
Same-source requirement (#4)
is_clean()/has_unresolved()and thescanCoverageJSON field both readRepinReport.outcome— one field, computed once inscan_coverage(). Nosecond tally exists anywhere in the module.
Surfaces that still lack the honesty bit (explicitly not done here)
evidence_query.rs'scoverage.status("complete"/"partial"string,computed via if/else, no
EvidenceScope, no enforced construction rule) —the closest existing analog to what this PR builds, and the most obvious
next candidate to migrate onto
EvidenceOutcome.change_risk's saturating caps (FILES_TOUCHED_CAP,LINES_CHANGED_CAP,BUG_MAGNET_CAP,CHURN_CAP) — silently saturate a score; no coverageclaim exists to convert yet, but a "did we see the whole diff shape or did
we cap out" bit would fit this type.
snapshot.rs's"[truncated]"bounded-preview helper — sametruncation shape, unconverted.
arbitration / in-flight-change registry) — not started; this PR is scoped
to G1's type and one proof surface only, per the charter's own layering.
capability_inventory.rs/sentrux_analysis.rs(the otherbug-magnet hotspots) — out of scope for this change, not examined for
this pattern.
Testing
cargo test --workspace --locked: 2958 passed, 0 failed.cargo fmt --check: clean.code-intel sentrux check .: all rules passed, god-file count unchanged(33 -> 33), no degradation against
.sentrux/baseline.json.Refs #139 #138 #133