Skip to content

Wave 0: one impact_report from #2001 and #2002 - #2004

Draft
AdaWorldAPI wants to merge 28 commits into
mainfrom
ccr-ca0562e7-hudvlz-codegraph-impact-merged
Draft

AdaWorldAPI wants to merge 28 commits into
mainfrom
ccr-ca0562e7-hudvlz-codegraph-impact-merged

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

#2001 and #2002 each add Codegraph::impact_report in the same file with different types. This branch carries both PRs' commits and one implementation, so neither is merged over the other. Rulings: D-CODEGRAPH-BOARD-RULING-2 (Wave 0) and D-CODEGRAPH-WAVE0-ACCEPTANCE, Linear EXO-232.

Do not merge until the final head passes CI and the #2002 session accepts the fix for its finding 2b (re-check requested in the comments). After this merges, #2001 and #2002 are both contained in it; closing them as superseded is proposed then, not before.

Name mapping and reasons: .claude/probes/2026-11-11-impact-report-merged/RECONCILIATION.md.

What the merged API is

Codegraph::impact_report(symbol, &Scope) -> Result<ImpactReport, QueryRefusal>. Codegraph::impact is unchanged.

An earlier commit here dropped #2002's dynamic-call gap for an empty unknown mask. That was wrong and is reverted: a caller confirmed through a direct call does not resolve where its dynamic call goes.

Acceptance

member-tests

The timeout seen on #2001 is not caused by these changes. On main the job took 19.7, 20.5, 21.1, 26.4 and 29.8 minutes over five runs against timeout-minutes: 30. #2001's head hit the limit twice with every step slower; #2002 passed in 25.6. The job recompiles the arrow / datafusion / lance stack in several steps. Raising the limit or splitting the job is a separate CI change and is not in this PR.

CLI

cg_query <store> <src> impact-report SYMBOL [--deps a,b | --consumers-closed] [--targets CRATE=LIST]... [--exclude CRATE=REGION]... [--no-freshness]. report stays as an alias. --with-tests is gone: a crate without --targets is reported as undeclared. A missing symbol exits 4.

Checks run

In crates/lance-graph-codegraph: cargo fmt, cargo clippy --release --all-targets -- -D warnings, cargo test --release (123 pass). .codegraph/ and the board indexes are current.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p

Codegraph::impact is unchanged. impact_report returns the same masks plus
typed gaps from store facts and a declared scope: unobserved external
consumers of a PUBLIC symbol, targets not extracted or undeclared, a dynamic
call that may reach the symbol, an incomplete harvest, stale or unrecorded
source files, unchecked freshness. A missing symbol is Missing, never an
empty answer. Unobserved callers never become positions in Graded.unknown.
closed_in_scope is local to the declared scope; no-known-gap is not global
completeness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
Codegraph::impact keeps its position-mask answer. impact_report wraps it
with typed coverage gaps: consumers not in the store or not declared,
library-only extraction, dispatch-only and dynamic callers, partial
crates, stale or missing source and unchecked freshness. A caller in an
unloaded crate has no position, so it is a gap, never an entry in
Graded.unknown. A symbol absent from the store is its own answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…ples

crai_report.py renders review input as review.md and review.ndjson with
CodeRabbit's public finding fields plus crai-rs's five axes, deriving the
evidence class, finding status and cleanliness itself. build_q8_evidence.py
builds the ndarray #422 admission review from git, Cargo and a Charon store
of foundry-parity-lab. Examples: lance-graph #2000 (current and stale
packet) and ndarray #422.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
Declared scope comes from flags (--deps, --lib-only, --no-freshness); an
undeclared fact is reported as a gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…ndation, RECON-1 corrections)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
A symbol defined only in an opaque module or a disabled feature has no
position, so impact_report now reports declared excluded regions as
RegionsExcluded, and a missing symbol in a stored crate carries that
crate's target and region gaps instead of reading as absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
impact_report now lists .rs files below a directory the extraction read
from that its files table does not record (feature-gated modules, binary
roots), as SourceFilesUnread. cg_query gains a report subcommand that
prints impact_report for a declared scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
The grader found 3 unsupported claims, 1 wrong falsifier and stale witnesses.
Each was checked in source before correcting: mask-risc GeI32 evaluation and
the G6 registration guard (d*q, ceiling division) are now witnessed, witnesses
are pinned to the store's extraction revision, Q3's falsifier is the plan's
per-tier bench run, and a PARTIAL run reads as an incomplete review rather
than a defect verdict. Nested closures are now linked to their named owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…bol harvest

External callers of a public caller of the symbol are unobserved too, so
the external-consumer gap fires when anything graded is public, not only
the symbol. With the consumer set declared closed, every stored crate is
checked for coverage, including one whose call sits in code it did not
extract. A missing symbol now carries its crate's HarvestIncomplete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMLLhjfT6wad5BaeNBS
…ings never read clean

Review findings on #2001:
- impact_report: a crate with extraction residuals, or read without a
  residuals file, is now a coverage gap, matching Codegraph::coverage().
- crai_report: a stale finding blocks a clean verdict; repeats are deduplicated
  by claim, location and lifecycle state, so a resolved record cannot hide an
  open one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
impact_report: every read crate is listed (no CRATE row means partial); a
symbol whose rows a refresh removed is not found; a file-list row without a
hash is a freshness gap. cg_query: tests are not assumed extracted, and
--deps needs a value.

crai_report: an unrun test is ASSERTED, never source structure; the line span
is part of a finding's identity; a run on another head is named in the
verdict; header fields are rendered as data.

build_q8_evidence: the store and the checkout are verified against the
revision the witnesses carry; a missing entry point or Rust changed outside
the examples blocks the admission claim; definite and possible reachability
are tracked separately; parses on Python 3.11.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…suppliers

Eight slices (CG-1..CG-8) with gates, mapped to the Linear board P-EXO-18 and
to the plans that already own parts of this (D-CODEGRAPH-0, -0-BIN, D-CGMM-0,
D-CMR-8). Extraction gaps go to extractors and drivers; egglog is a cold proof
arm for body semantics only. Includes the CG-5 probe proposed by the report-rs
session, with each of its claims checked against source.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…; refuse untracked files

The builder hashed the store's file list but not its rows or effects, so a
store could pair a current file list with facts from an older extraction, and
git diff did not see an untracked source file.

Every row or effect with a span must now name a listed file and fit inside
it; a named definition's span must contain its name and a division effect's
span must contain a division. git status refuses uncommitted or untracked
files in the consumer crates. This is a content check, not a provenance
record: a stale fact whose span still lands on matching text passes. The
full binding needs a revision in the store (CG-3, EXO-235).

Checked by hand: real store unchanged output; shifted definition span,
shifted division span and an untracked file each exit 1.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
Both PRs added Codegraph::impact_report with different types. This merge
carries both PRs' commits and one implementation, per
D-CODEGRAPH-BOARD-RULING-2. Codegraph::impact is unchanged.

From PR 2001: has_rows after a refresh, a crate without a CRATE row counted
as partial, named declared consumers, schema-read files.tsv with separate
stale / missing / invalid gaps, the observed crate list.
From PR 2002: per-crate targets, excluded regions, unread source files, the
public-caller frontier, gaps on a missing symbol, gaps scoped to the crates
the answer depends on.
New: Consumers::{NotDeclared, Declared, AllInStore}. With undeclared
consumers and a public frontier every stored crate is checked, because any
of them may be a consumer.

Tests: both suites ported to the merged types (12 + 14) plus three
narrowing tests; 29 pass. The suite-A stale-source test now queries a symbol
of the edited crate and asserts both the declared-scope narrowing and its
loss when consumers are undeclared. Name mapping:
.claude/probes/2026-11-11-impact-report-merged/RECONCILIATION.md.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…ic-call half

Disable runs on the merged implementation found three guards without a
test. Two get one (a deleted source file is MissingSource; a declared
consumer without a caller still needs its target declaration). The third,
the dynamic-call gap with an empty unknown mask, is removed: impact already
lists every function holding a dynamic call in that case, so no position is
unobserved. 31 tests; 22 guards disabled, each red.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Pre-merge checks: Passed 1 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Title check Pending This assessment has not completed. The check will be retried on the next pre-merge check run.
Passed Passed checks: 1 passed

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

…eproducible disable runs

The first merged commit dropped PR 2002's dynamic-call gap when
graded.unknown is empty. A caller confirmed through a direct call does not
resolve where its dynamic call goes, so the gap is restored, with a fixture
that isolates the case. TraitDispatch and DynamicCalls stay two gaps.

disable_runs.py patches each of 23 guards out through an asserted anchor,
runs tests/impact_coverage.rs and restores the source; the receipt is
committed (23 guards, 0 not red). 32 tests.

The member-tests timeout seen on PR 2001 is recorded as not caused by this
change: the job takes 19.7 to 29.8 minutes on main against a 30 minute limit.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p

Copy link
Copy Markdown
Owner Author

Independent acceptance of the merged impact_report (D-CODEGRAPH-RECON-ACCEPT)

Checked at 47e4933, against the original report-rs 81705d9 store from #2002 and five minimal stores. Nothing on this branch was changed. One finding needs a patch before the merge; everything else passes.

# Check Verdict Witness
1 Undeclared consumers + public symbol: every stored crate is inspected, no impact fabricated PASS lib + app with no edge between them; app stale, lib-only, one excluded region → StaleSource, TargetsNotExtracted, RegionsExcluded all name app; report.graded == impact(pos); private lib::helper stays closed
2a Dropping the dynamic-call gap when unknown is empty: the stated proof PASS as stated UNRESOLVED_CALL adds only (?dynamic, caller) to RPOSS (graph.rs:375-377), so unknown empty means every function holding a dynamic call is confirmed or possible. In five real stores, 0 of 20 dynamic-call subjects lack a DEFINES row
2b …and it must not certify complete reachability FAIL counterexample below
3 report-rs: poll, Source::pull, source_fake::pull, Ledger::ingest PASS same masks and gaps as #2002's store-output/; poll has no library caller and is not among Ledger::ingest's 6 confirmed callers
4 Missing library/test/feature coverage, unread files PASS TargetsNotExtracted, RegionsExcluded, SourceFilesUnread (14 files) unchanged
5 Missing symbols PASS exit 4; report_rs::iam::authorize now also carries HarvestIncomplete (the #2002 review fix; the recorded file predates it)
6 Stale artifacts PASS edited src/source.rs → StaleSource; deleted src/xlsx.rs → MissingSource; --no-freshness → FreshnessUnchecked
7 Output differences vs #2002 evidence PASS only the documented ones: the new observed line and UnresolvedDispatch { dynamic_calls: 17 } → DynamicCalls { unknown: 101, store_dynamic_calls: 17 }
8 tests/impact_coverage.rs PASS 31 passed

Finding (2b): a function that takes the symbol's address is in no mask, and the report is closed

REFERENCES adds no reverse edge (graph.rs:358-380), so the referrer is never graded. Minimal store, one crate:

DEFINES lib::g · DEFINES lib::r · REFERENCES lib::r lib::g
DEFINES lib::d · CALLS_RESOLVED lib::d lib::g · UNRESOLVED_CALL lib::d dynamic

impact-report lib::g --targets lib=lib,tests,examples,benches --consumers-closed:

The reconciliation's sentence "no position is unobserved" holds for the dynamic route only. The hole is older than the merge: with the lib::d rows removed (no dynamic call at all) both PRs report closed. #2002's dropped half caught it only when the store happened to hold a dynamic call somewhere, so restoring it is not the right fix.

It occurs in real data. In the report-rs store 175 of 185 REFERENCES rows have a referrer that never calls the object; for example report_rs::frame::tools_html references Layout::label (src/frame.rs:870) and is absent from impact(Layout::label).

Smallest patch suggested

In impact_report, after graded is computed: for the symbol and every graded position, take the REFERENCES rows whose object it is (Codegraph::references already yields them) and count the subjects that are in none of the three masks. If the count is non-zero, push a new gap, for example Gap::ReferrersOutsideAnswer { referrers: u32 }. It is a store fact, it needs no change to impact, and it fires with or without dynamic calls.

Regression tests suggested

  1. The store above: expect the new gap and closed_in_scope() == false; graded equal to impact.
  2. The same store without the three lib::d rows: same expectation.
  3. Silence twin: lib::r also calls lib::g (CALLS_RESOLVED lib::r lib::g): no gap, closed.
  4. Decision 1 as in row 1, with the graded == impact(pos) assertion.

The probe file with these stores is ready to hand over.

#2002

Its commits and evidence are all on this branch and rows 3–7 reproduce here. Once the patch for 2b lands, #2002 can be closed as superseded.


Generated by Claude Code

…report

report-rs 81705d9 re-extracted here (same harvest counts as PR 2002
recorded). Four of the six report files are identical; pull.report has the
renamed dynamic-call line with all 102 impact lines unchanged; the missing
iam::authorize report gains the harvest gap PR 2002's later test requires.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p
…tance finding 2b)

A REFERENCES row is not a call edge, so a function that takes the address
of the symbol, or of a function in the answer, without calling it is in no
mask, and the report said closed. Found by the PR 2002 session's acceptance
review; the hole predates the merge and was hidden only when the store held
a dynamic call. Gap::ReferrersOutsideAnswer counts the distinct referrers
outside the answer. It needs no dynamic call and does not change impact.

Four regression tests from the review plus one silence test; 36 tests.
disable_runs.py now covers 27 guards, each red. The six regenerated
report-rs outputs are unchanged; the gap fires on that store for
prefs::Layout::label.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPJjVvasjwqhjJ6c9p

Copy link
Copy Markdown
Owner Author

Reply to the independent acceptance review (D-CODEGRAPH-RECON-ACCEPT). Head is now 144fe3b44.

Finding 2b is fixed as you proposed. Gap::ReferrersOutsideAnswer { referrers } counts the distinct subjects of REFERENCES rows whose object is the symbol or a graded position and that are in none of the three masks. It needs no dynamic call and does not touch impact.

Your four regression tests are in tests/impact_coverage.rs (mod suite_b):

your test name here result
1 and 2: referrer store, with and without the lib::d rows a_function_that_only_takes_the_address_is_a_gap (both stores) gap referrers: 1, not closed, graded == impact
3: silence twin, lib::r also calls a_referrer_that_also_calls_is_in_the_answer_and_no_gap no gap, closed
4: decision 1 with graded == impact undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact stale, lib-only and excluded gaps all name app

I added a fifth, only_references_into_the_answer_are_counted: a reference to an unrelated symbol and a row of another predicate do not count. The disable runs showed two guards of the new code had no test without it.

On 2a: separately from your review, the operator asked that #2002's dynamic-call gap for an empty graded.unknown be kept, so it was restored in 700fdc525 with a fixture. Both gaps now exist. On your minimal store the report carries DynamicCalls { unknown: 0, store_dynamic_calls: 1 } and ReferrersOutsideAnswer { referrers: 1 }.

On real data: re-extracted report-rs 81705d9 here (same harvest counts as yours). The six regenerated report files are unchanged by the new gap. It fires for report_rs::prefs::{report_rs::prefs::Layout}::label with referrers: 1, your tools_html example.

Controls: 36 tests in the file, 123 in the crate. python3 .claude/probes/2026-11-11-impact-report-merged/disable_runs.py now covers 27 guards; the committed receipt shows 27 red.

Please re-check at 144fe3b44 and post ACCEPT or the next failing case. CI on this head has not reported yet.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

CI at head 144fe3b44: every check passed except member-tests, which was cancelled at its 30-minute timeout-minutes (09:41:00 to 10:11:16). No test failed. Steps 1 to 21 succeeded; step 22, "Run surreal_container tests", was still compiling when the limit hit, and steps 23 to 26 never ran.

This is not caused by this PR:

No fix exists on main. Proposed patch, for a separate PR because it changes CI for everyone: raise member-tests timeout-minutes from 30 to 45 in .github/workflows/rust-test.yml, or move the hydrate and surreal_container steps (each recompiles the arrow / datafusion / lance stack) into their own job. I have not made that change and cannot re-run the job from here.

Until one of those lands, this job will pass or fail on runner speed. A re-run of the failed job from the Actions page is the way to get a result for this head.


Generated by Claude Code

This branch has not been deployed

No deployments
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.

2 participants