Skip to content

feat(gate): G1 -- computed honesty bit for repin's scan coverage - #141

Merged
2233admin merged 1 commit into
mainfrom
g1/computed-honesty-bit
Aug 3, 2026
Merged

feat(gate): G1 -- computed honesty bit for repin's scan coverage#141
2233admin merged 1 commit into
mainfrom
g1/computed-honesty-bit

Conversation

@2233admin

Copy link
Copy Markdown
Owner

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) — a
completeness claim for one evidence-producing surface, with three states:

  • Complete(EvidenceScope) — the surface was fully enumerated. The only
    constructor for this variant takes an EvidenceScope (artifact types
    consumed, paths scanned, excluded prefixes); there is no way to build a
    Complete in Rust without one.
  • Partial { reason: PartialReason, scope: EvidenceScope } — reason is
    Truncated, CapHit, or UnreadableInput(paths); scope says what was
    covered 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, and
repin.rs before 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 coverage
claim — there's no "clean"-style pass/fail bit being asserted there today.

repin is where the silent-lie failure mode is real and already reported
(#133): repin --write --json printed "clean": true, "filesChanged": 0
while digests it never scanned were stale. Reading repin.rs end to end, I
found 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):

# exclude the only tracked file out of the scan surface
$ code-intel repin --repo <repo-with-one-file> --exclude only.rs --json
{ "clean": true, "filesChanged": 0, ... }   # exit 0

A scan that covered zero files reported exactly the same clean: true
shape 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.rs was also not the concurrently-edited file (native_code_evidence.rs
chunk-emission work is untouched by this PR — confirmed via git diff --stat
before push).

What changed in repin

RepinReport now stores one EvidenceOutcome, computed once in
scan_coverage() from the exact same scan_targets / skipped data every
other field on the report is built from:

  • Empty scan_targets after exclusions → NotComputed (the bug above).
  • Any gates_clean skip (too-large / unreadable input) → Partial with
    UnreadableInput and the paths that couldn't be read.
  • Otherwise → Complete, with the scope (artifact_types: ["tracked-text-file"],
    the scanned paths, the exclude prefixes).

Both the JSON scanCoverage field and is_clean()/has_unresolved() (which
drives 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_rejected in
crates/code-intel-cli/tests/repin.rs, plus unit tests in
evidence_outcome.rs. Two forgeries, not one:

  1. A real not-computed report (from the empty-scan-surface case above),
    status flipped to "complete" with no scope added at all.
  2. The realistic one: a real partial report (oversized-input case),
    status flipped to "complete" — leaving its genuine, fully-populated
    scope and its partialReason/paths fields completely untouched. This
    is the shape bug(repin): operationTrace 与 evidenceIds 内嵌 digest 不在扫描面——clean 报告失真 #133's own bug took: one field changed, everything else
    left alone. A bare "does scope exist and have its fields?" check would
    accept this forgery, because the scope really is there and really is
    well-formed (just borrowed from a claim that never achieved completeness).
    from_json catches it with an exact-key-set check per status — a
    Partial's object is a strict superset of a Complete's, so the leftover
    partialReason/detail/paths fields 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_json on the forged-from-partial JSON returned
Ok(Complete(...)) instead of Err(...), and both the integration test and
the unit test failed as expected. Re-added the check, both pass again.

Same-source requirement (#4)

is_clean() / has_unresolved() and the scanCoverage JSON field both read
RepinReport.outcome — one field, computed once in scan_coverage(). No
second tally exists anywhere in the module.

Surfaces that still lack the honesty bit (explicitly not done here)

  • evidence_query.rs's coverage.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 coverage
    claim 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 — same
    truncation shape, unconverted.
  • G2 (detector registry + gating function) and audit: detector 注册表 + 覆盖矩阵推导 + N-agent 仲裁面——把 prompt 审计换成可复现证据 #138 section C (N-agent
    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.
  • Did not touch capability_inventory.rs / sentrux_analysis.rs (the other
    bug-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

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
@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 3, 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 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

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 @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: 5b9de710-a27a-4a08-b207-fc094a41e521

📥 Commits

Reviewing files that changed from the base of the PR and between 6a6dd20 and 98fd658.

📒 Files selected for processing (6)
  • crates/code-intel-cli/src/evidence_outcome.rs
  • crates/code-intel-cli/src/main.rs
  • crates/code-intel-cli/src/repin.rs
  • crates/code-intel-cli/tests/cli_head_parity.rs
  • crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json
  • crates/code-intel-cli/tests/repin.rs

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 3, 2026

Copy link
Copy Markdown

Code Intel change risk

Score Percentile Level
64/100 53th (vs last 49 commits) 🟢 low

Top signals

  • Diff shape: 6 file(s), +799/-35 (max file share 0.56)
  • Test asymmetry: source changed, tests touched
  • Bug-magnet: 7 fix commit(s) in touched files (180d)
  • Churn: 35 commit(s) touching these files (90d)

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

@2233admin
2233admin merged commit ade9e45 into main Aug 3, 2026
8 checks passed
2233admin added a commit that referenced this pull request Aug 4, 2026
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
2233admin added a commit that referenced this pull request Aug 4, 2026
…#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
@2233admin
2233admin deleted the g1/computed-honesty-bit branch August 7, 2026 10:38
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