diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 66f64fc64..2343a2e92 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,8 @@ +## 2026-11-11 — D-CODEGRAPH-EXPAND-1: what the Codegraph still needs, and who supplies it → `.claude/plans/codegraph-expansion-v1.md` + +**Status:** PLAN. Eight slices CG-1..CG-8 on Linear `P-EXO-18` (EXO-233..EXO-239, EXO-241; channel EXO-232; handover EXO-240). **Board:** [STATUS_BOARD](STATUS_BOARD.md) D-CODEGRAPH-EXPAND. Follows #1999, #2000, #2001; supplies inputs to D-CODEGRAPH-0, D-CODEGRAPH-0-BIN and D-CGMM-0. +**Confidence:** every gap is measured in #2000 / #2001; the egglog, DialEgg and Kythe parts are hypotheses, unread. + ## 2026-11-10 — D-FOUNDRY-PARITY-1: DR × DT64 as an operational ontology → `.claude/plans/foundry-parity-v1.md` **Status:** ACTIVE; Level R lab landed (#1991) and P1a arm C per-store cost (#1995); P1b (min/argmin) open. Evidence matrix over 18 Foundry capability rows, missing-contract register G1–G6, roadmap P0–P5. [entry](entries/2026-11-10-foundry-parity-1-lab.md) · [P1a](entries/2026-11-10-foundry-parity-1-arm-c.md) · STATUS_BOARD D-FOUNDRY-* · Linear EXO-223. diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index c8959c117..be312e78a 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -360,6 +360,23 @@ Plan: `.claude/plans/d-soc-fence-v1.md`. Plan only; nothing runs yet. **Evidence boundaries:** D-SAG-6 #1760 merged docs; Math Foundry #1693/#1694 E1 lab merged; D-NFC #1753 native-first; #1775 D-DRAV draft / AV1–AV12 NOT RUN; MORK oracle with unresolved production licence/allocator; EXO-148 stable durable types unresolved. No complete distributed reasoning implementation or measured AI/data-center scaling claim. +## D-CODEGRAPH-EXPAND — measured gaps after #2001, and their suppliers (2026-11-11) + +Plan: `.claude/plans/codegraph-expansion-v1.md`. Coordination: Linear `P-EXO-18`. + +| D-id | Deliverable | Status | +|---|---|---| +| **D-CODEGRAPH-RECON-2-A** | coverage-aware `impact_report`, `cg_query impact-report` | In PR: #2001 | +| **D-CRAI-REPORT-1** | read-only review report generator, ndarray #422 admission case | In PR: #2001 | +| **CG-1** | closure-ownership edge, graded POSSIBLE | Queued (decision needed): EXO-233 | +| **CG-2** | extraction driver from `cargo metadata` (targets, dependents, tests) | Queued: EXO-234 | +| **CG-3** | store provenance: extraction revision, relative generated paths | Queued (format decision needed): EXO-235 | +| **CG-4** | edge vocabulary mapped to Kythe names (document) | Queued: EXO-236 | +| **CG-5** | co-research: body semantics through R2IL and egglog | Queued (needs go-ahead): EXO-237 | +| **CG-6** | review report follow-ups (mask-risc extraction, re-grade, placement, advisory CI) | Queued: EXO-238 | +| **CG-7** | report-rs #33 transfer case | Other session: EXO-239 / EXO-185 | +| **CG-8** | effects, target kind and cfg as Codegraph rows | Queued: EXO-241 | + ## D-CODEGRAPH-0 — resident Codegraph: Charon → ruff → lance-graph + crai-rs (2026-11-02) Plan: [d-codegraph-0-resident-codegraph-v1.md](../plans/d-codegraph-0-resident-codegraph-v1.md). Entry: [gap map](entries/2026-11-02-d-codegraph-0-gap-map.md). diff --git a/.claude/board/entries/2026-11-11-codegraph-recon-2.md b/.claude/board/entries/2026-11-11-codegraph-recon-2.md new file mode 100644 index 000000000..05db99f13 --- /dev/null +++ b/.claude/board/entries/2026-11-11-codegraph-recon-2.md @@ -0,0 +1,25 @@ +# 2026-11-11 — Coverage-aware impact; report-rs #33 recovered from compiler facts + +**Status:** MEASURED (probe). `.claude/probes/2026-11-11-codegraph-recon-2/`. Follows +`2026-11-11-codegraph-recon-1` (#2000). Board: EXO-229, EXO-230. + +`Codegraph::impact_report` (additive; `impact` unchanged) returns `impact`'s masks plus typed gaps: +external consumers unobserved, targets not extracted or undeclared, excluded regions, unresolved +dispatch, incomplete harvest, stale or unrecorded files, and `.rs` files under a read directory that +`files.tsv` does not list. Missing coverage never enters `Graded.unknown`. A missing symbol carries +its crate's gaps. 14 tests; every guard disable-verified red. + +report-rs `81705d9` through Charon (lib, default features, `crate::app` opaque): CRATE `COMPLETE`, +yet 14 of 59 `src/` files unread (`iam.rs`, `main.rs`, `project_plan/*`, …). The store recovers the +first #33 finding: `workbook_delta::WorkbookSource::poll` has no library caller and no path to +`Ledger::ingest`; `Ledger::pull → source_fake::pull` is dynamic and reported UNKNOWN. + +Blind arms graded strictly against the EXO-185 key (12 items): source arm 12 correct; store arm +7 correct, 3 partial, 2 unsupported; 0 wrong in both. The store's misses are configuration +(feature-gated `iam`), targets (test callers) and manifest facts (Cargo.toml). n = 1 per arm. + +#2000 reproduces from its documented commands: rows equal, `machine-recovered.mmd` byte-identical, +Charon recall column identical. + +OPEN: per-configuration stores (features, test targets); recording targets and opaque modules in +the store instead of declaring them. diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 6a6e823bb..cf28e5540 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,10 +25,11 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately opposite directions; the stranding this convention prevents shows up in exactly one of them, never both. -597 entries, 2026-08-06 .. 2026-11-11. +598 entries, 2026-08-06 .. 2026-11-11. | date | entry id | finding | file | |---|---|---|---| +| 2026-11-11 | `codegraph-recon-2` | | [2026-11-11-codegraph-recon-2.md](2026-11-11-codegraph-recon-2.md) | | 2026-11-11 | `codegraph-recon-1` | | [2026-11-11-codegraph-recon-1.md](2026-11-11-codegraph-recon-1.md) | | 2026-11-10 | `lab-resolve-borrow-all` | | [2026-11-10-lab-resolve-borrow-all.md](2026-11-10-lab-resolve-borrow-all.md) | | 2026-11-10 | `int8-direct-packed-b` | | [2026-11-10-int8-direct-packed-b.md](2026-11-10-int8-direct-packed-b.md) | diff --git a/.claude/plans/codegraph-expansion-v1.md b/.claude/plans/codegraph-expansion-v1.md new file mode 100644 index 000000000..899feb5ff --- /dev/null +++ b/.claude/plans/codegraph-expansion-v1.md @@ -0,0 +1,116 @@ +# D-CODEGRAPH-EXPAND-1: what the Codegraph still needs, and who supplies it (plan v1) + +> **2026-11-11 · PLAN.** Coordination: Linear project **Codegraph · Collaboration Board** (`P-EXO-18`), +> channel EXO-232. GitHub is canonical; Linear records owners, evidence links and open decisions. +> Follows #1999, #2000 and #2001. Every gap below was measured there; every statement about +> egglog, DialEgg and Kythe is a hypothesis until its slice has read the source. + +## 1. Question + +After #2001 the Codegraph answers callers, callees and impact over real Charon stores, and says what +it could not see. Which gaps remain, which can be closed by hand-written edges, and where do R2IL, +egglog (mlir-egglog, eggcc, DialEgg) and Kythe fit? + +## 2. Answer in one table + +| gap | measured | kind of fix | supplier | slice | +|---|---|---|---|---| +| closure bodies have no edge from their parent | 96 in foundry-parity-lab; caused a missed division | derived edge, graded POSSIBLE | Codegraph crate | CG-1 | +| consumer crates not in the store | 13 of 14 consumers of `admit` | extract dependents | extraction driver | CG-2 | +| test targets and examples not extracted | 48 test sites for `LaneBinding::check`, none resolved | extract those targets | extraction driver | CG-2 | +| wrong Cargo target extracted | `bench` binary instead of the library | name the target; residuals are a refusal | extraction driver | CG-2 | +| dependents list built by hand | a wrong-column list removed a real gap | take it from `cargo metadata` | extraction driver | CG-2 | +| store does not record its revision | witnesses stamped with the wrong revision | provenance in the store | ruff_charon | CG-3 | +| absolute paths in the store | same input, different digest at another target dir | paths relative to `OUT_DIR` | ruff_charon | CG-3 | +| Charon error inside a crate | r2il is PARTIAL on a `dyn Fn` type alias | minimal case, upstream or typed gap | Charon | CG-3 | +| effects are extracted but not queryable | 213 effect rows in the foundry store; the Codegraph reads none | reader in the Codegraph | Codegraph crate | CG-8 | +| target kind, cfg and feature are not rows | "example-only" was read from `Cargo.toml` by hand; 32 inactive regions skipped | hand-rolled rows | `cargo metadata`; cfg source undecided | CG-8 | +| no def-use, no loop multiplicity | "the divisor is `per_staff`" was read by eye | new row kind from LLBC | ruff_charon | CG-5 | +| edge names are local | no shared vocabulary with other tools | mapping to Kythe edge kinds | document | CG-4 | +| what a body means | divisions classified by a source-span regex | rules over a lowered body | R2IL + egglog, unproven | CG-5 | +| review evidence is partial | mask-risc read as source; corrected review not re-graded | extract, test, re-grade | review report | CG-6 | +| one repository only | no held-out manual review yet | second repository | report-rs #33 | CG-7 | + +**The split that decides everything else:** extraction gaps (CG-1 to CG-3) are closed by extractors +and drivers. An e-graph proves that two expressions are equivalent; it cannot supply a call edge that +was never emitted. So egglog is a candidate for body semantics (CG-5) only. + +## 3. Slices + +| slice | Linear | owner repo | depends on | gate | +|---|---|---|---|---| +| CG-1 closure-ownership edge | EXO-233 | lance-graph | #2001; a decision on mask vs separate relation | `can_cook_guard` impact includes `methods` as POSSIBLE; closure-free twin gains nothing; disable run red | +| CG-2 extraction driver | EXO-234 | lance-graph, uses ruff_charon | #2001 | `admit` reports no `ConsumersNotInStore`; residuals or a non-COMPLETE crate give PARTIAL, never empty success | +| CG-3 store provenance | EXO-235 | ruff, checked in lance-graph | a format decision | two extractions at different target dirs are byte-identical; a store at another revision is refused | +| CG-4 Kythe edge vocabulary | EXO-236 | lance-graph, document | CG-1 | every row cites the Kythe schema and a Codegraph source line | +| CG-5 body semantics, co-research | EXO-237 | research | operator go-ahead | one rule classifies the two ceiling divisions and the oracle quotient of #422; disable run red | +| CG-6 review report follow-ups | EXO-238 | lance-graph; crai-rs owns semantics | #2001 | mask-risc store exists; builder has negative controls; two operator decisions recorded | +| CG-7 report-rs #33 transfer | EXO-239 (record: EXO-185) | report-rs session | #2001; CG-2 if needed | strict grading against the held-out manual review | +| CG-8 effects, target kind, cfg as rows | EXO-241 | lance-graph | #2001; shares `cargo metadata` with CG-2 | "which divisions does `max_quantities` reach" is answered inside the Codegraph crate; a missing effects file is a gap | + +Handover prompt for the CG-7 session: EXO-240. + +**Order.** CG-1, CG-2 and CG-8 first; they are small, measured and independent. CG-7 can start now from the +#2001 branch. CG-3 waits for a format decision. CG-4 is a document and can run beside anything. CG-5 +needs a go-ahead and is research before it is a build. + +## 3a. The CG-5 probe (proposed by the report-rs session, checked 2026-11-11) + +Recover the ndarray #422 answer with no manual reading: + +1. target-kind and cfg rows (CG-8); +2. def-use rows from LLBC through ruff_charon (a new row kind, so it needs the CG-3 format decision); +3. a small egglog rule pack: reachable from the entry point, contains a division, divisor traces to + `per_staff`. + +**Pass:** it reports `arm_b.rs:245` and `method.rs:58`, each with a proof. +**Fail:** removing the division in a scratch copy leaves the answer unchanged. + +Checked against source before adoption: +- **Confirmed.** `mlir-egglog` here is a Python toy compiler for NumPy expressions; DialEgg is not + checked out; `egglog` and `eggcc` are. `crates/egglog-r2il-probe` exists. `KYTHE.md` records 24 + agreeing symbols and no `ref/call` edges. The divisor is `per_staff: 4 + (m as i32 % 5)` + (`fixture.rs:105`). +- **Corrected.** The store has ten predicate codes, not six. Division sites needed no token scan: + ruff_charon already emits them as `ASSERT DivisionByZero` effects, and what is missing is a reader + (CG-8). Charon is built on this machine but is not on `PATH`. +- **Agreed.** No MLIR in the path: Rust does not lower to it, so it would be a third representation. + What transfers from DialEgg is the pattern, one egglog term per IR operation, applied to LLBC. + egglog derives and proves; it is a cold oracle arm, never a per-query path, because the Codegraph + already computes closure over masks. + +## 4. Mapping to plans and issues that already exist + +This plan adds no second Codegraph programme. It supplies measured inputs to these: + +| existing | what it already owns | relation to this plan | +|---|---|---| +| D-CODEGRAPH-0 (`d-codegraph-0-resident-codegraph-v1.md`, EXO-229) | Charon → ruff → resident rows, queries | CG-1 and CG-2 are its next increments | +| D-CODEGRAPH-0-BIN (`d-codegraph-0-binary-lanes-v1.md`) | binary lanes, a provenance header, per-row `(artifact, FunDeclId)` | CG-3 is the text-store half of the same provenance question; decide them together | +| D-CODEGRAPH-0-D | a local stdio MCP over the queries | not in this plan; `cg_query` is its CLI precursor | +| D-CGMM-0 (`d-cgmm-*.md`, EXO-155) | Kythe-shaped immutable evidence, lazy demand, FieldViewDef, DT64 binding | CG-4 feeds D-CGMM-1 (source facts); CG-1 feeds D-CGMM-8 (evidence and call graph) | +| EXO-85, EXO-84 | MeTTa / Datalog assimilation, native polyfill | CG-5 must not duplicate these; it asks one narrower question | +| EXO-73, EXO-101 | recursion driver, datafrog parity | unaffected; the Codegraph closure is not a new recursion driver | +| D-CMR-8 (`d-cmr-concept-chain-normalization-v1.md`) | equality-saturation probe, `egg` / `egglog` as references only | CG-5 reuses its stance: reference and probe, not a runtime | +| `ascent-byods-dr-fold-polyfill-v1.md`, entry 2026-10-21 | earlier evaluation of egglog, datafrog and Ascent | CG-5 starts from that evaluation | +| D-FOUNDRY-PARITY-1 (EXO-227) | G1 to G6 | CG-6 carries the open #422 verification | +| EXO-230 | navigation and the review evidence packet | CG-6 | +| EXO-185 | report-rs #33 | CG-7 | + +## 5. Constraints + +- No new graph database, no second Rust parser, no blocking CI reviewer. +- These change only by a decision issue in `P-EXO-11`: the Charon and ruff_charon artifact formats, + the lance-graph-contract / ClassView API, DT64 admission, Quack / mask-RISC execution, XLOG + semantics, crai-rs review semantics. +- A name-derived edge is POSSIBLE, never CONFIRMED. +- Nothing from CG-5 enters a per-query path. Cold costs are reported in microseconds and are not gates. +- Every guard has a test that fails when the guard is disabled. + +## 6. Open decisions + +1. May a name-derived closure edge enter `impact`'s masks, or is it a separate relation (CG-1)? +2. May the ruff_charon store format gain a revision and relative paths, and is that decided together + with D-CODEGRAPH-0-BIN's provenance header (CG-3)? +3. Go-ahead for the CG-5 co-research pass. +4. Where the review report generator lives, and whether the advisory CI job is built (CG-6). diff --git a/.claude/probes/2026-11-11-codegraph-recon-1/REPORT.md b/.claude/probes/2026-11-11-codegraph-recon-1/REPORT.md index eebc08d9c..63b69cbd0 100644 --- a/.claude/probes/2026-11-11-codegraph-recon-1/REPORT.md +++ b/.claude/probes/2026-11-11-codegraph-recon-1/REPORT.md @@ -241,3 +241,31 @@ GRAPH=1 crates/sag-ra-probe/target/release/sag-ra-probe "$PWD" > ra_graph.tsv cargo run --release --manifest-path crates/lance-graph-codegraph/Cargo.toml --example cg_query -- $STORE . sweep python3 .claude/probes/2026-11-11-codegraph-recon-1/recon.py score --store $STORE --src . --corpus corpus.tsv --ra ra_graph.tsv ``` + +## Reproduction check (before merge) + +The documented commands were re-run from scratch into a fresh directory at `90fc5a7`: Charon and +`ruff_charon import` on the four crates, then rust-analyzer `GRAPH=1`. The results were compared with +the artifacts this report was built from. + +- `machine-recovered.mmd`, `reconciled.mmd` and `score.tsv` regenerate **byte-identical**, and + `cg_query sweep` gives the same 9,138 positions / 9,569 answered / 8,707 empty. +- **Text rows:** byte-identical for dt64, quack and r2il. Contract's rows and `files.tsv` differ only + in the absolute path of two build-script outputs (`manifest_metadata.rs`, `ogit_namespace.rs` under + `CARGO_TARGET_DIR/…/build/lance-graph-contract//out/`). Their content hashes are identical, + and after normalising that path the rows are identical. +- **LLBC:** byte-identical except for one embedded `--dest-file` path. +- **`rows.bin`:** differs only in its provenance block (the LLBC digest and the Charon config string, + which both carry that path) and in the header digest that covers it. +- **rust-analyzer graph:** identical apart from its `*_ms` timing rows (0 of about 820k rows differ). + +**Finding:** the store is reproducible in content but not location-independent. Paths of generated +files and the LLBC location are recorded verbatim, so the same input at a different `CARGO_TARGET_DIR` +gives a different digest. Two consumers are affected differently: +- **Refresh** compares store digests, so it treats the two stores as different. +- **The freshness check** (`impact_report`) does not compare digests. It hashes each file listed in + `files.tsv`. A recorded path that no longer exists is reported as `MissingSource`; a path that still + exists with the same content gives no gap, whatever the store digest. + +Owner: EXO-229 (ruff_charon could record generated files relative to +`OUT_DIR`). diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/GRADES.tsv b/.claude/probes/2026-11-11-codegraph-recon-2/GRADES.tsv new file mode 100644 index 000000000..ae87cf288 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/GRADES.tsv @@ -0,0 +1,26 @@ +#version 1 +#schema key arm grade note +K1.1 M CORRECT both identities, source_fake.rs:19 and workbook_delta/source.rs:210 +K1.1 C PARTIAL both identities CONFIRMED; defining lines UNKNOWN (the store records spans, not struct lines) +K1.2 M CORRECT source_fake.rs:29 impl Source +K1.2 C CORRECT IMPLEMENTS rows; witness source_fake.rs:39 (the pull fn, not the impl line) +K1.3 M CORRECT inherent poll, returns Poll, no Source impl +K1.3 C CORRECT no Source impl for the delta type, ABSENT-IN-SCOPE +K1.4 M CORRECT Ledger::pull calls src.pull (333) then ingest (334); projection.rs:409 passes source_fake +K1.4 C CORRECT ingest edge CONFIRMED at source.rs:334; dyn Source call reported UNKNOWN, not CONFIRMED +K1.5 M CORRECT no src caller of poll; callers in tests/p20 and examples/workbook_sync.rs; lands in Workbook::ingest +K1.5 C PARTIAL poll has 0 callers in the lib scope and no edge to Ledger::ingest; test/example callers not named (targets not extracted, reported as a gap) +K1.6 M CORRECT does not claim poll has no callers at all +K1.6 C CORRECT ABSENT-IN-SCOPE with tests/examples UNOBSERVED; never "no callers" +K2.1 M CORRECT iam.rs:250, behind feature iam (lib.rs:22-23) +K2.1 C UNSUPPORTED iam.rs reported unread, feature off, call UNOBSERVED +K2.2 M CORRECT no authorize_scoped anywhere +K2.2 C CORRECT ABSENT-IN-SCOPE, with the iam build UNOBSERVED +K2.3 M CORRECT gating stated; default build does not compile iam +K2.3 C CORRECT does not claim report-rs never authorizes +K3.1 M CORRECT no dt64-lance dependency (Cargo.toml), no MapLog in any file +K3.1 C PARTIAL no MapLog symbol in scope; the dependency fact is outside the store and not stated; lance-feature build left UNOBSERVED +K3.2 M CORRECT IdentityFrame holds MapTable (frame.rs:55-57), advanced in commit (189-203) +K3.2 C CORRECT MapTable::new frame.rs:69, advance frame.rs:195 in IdentityFrame::commit, called from ingest.rs:264 +K3.3 M CORRECT absence stated over every file and Cargo.toml, independent of features +K3.3 C UNSUPPORTED did not frame it as a dependency-scope fact diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/REPORT.md b/.claude/probes/2026-11-11-codegraph-recon-2/REPORT.md new file mode 100644 index 000000000..e6376d14b --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/REPORT.md @@ -0,0 +1,152 @@ +# D-CODEGRAPH-RECON-2: coverage-aware impact, and report-rs #33 from compiler facts + +D-CODEGRAPH-RECON-1 (#2000) showed that `Codegraph::impact` returns an empty set for a public +symbol whose callers live in crates the store does not hold. This probe keeps `impact` as it is +and adds `Codegraph::impact_report`, which returns the same masks plus a typed list of what the +answer could not see. It then answers three questions about report-rs PR #33 from a Charon store +and grades the answers against the manual EXO-185 findings, which were held out. + +Result: the store recovers the first #33 finding (the SharePoint drive source never reaches +`Ledger::ingest` from library code), explains it correctly, and names every region it could not +see. Both arms made no wrong statement. The source-reading arm was more complete, because the +store sees one build configuration and one target. + +## 1. `impact_report` (Gate A) + +`crates/lance-graph-codegraph/src/impact_report.rs`. `Codegraph::impact` is unchanged and +`ImpactReport.graded` is exactly its output. Missing coverage is never written into +`Graded.unknown`, which stays "observed functions a dynamic call may reach". + +| gap | source of the fact | +|---|---| +| `ExternalConsumersUnobserved` | the symbol or anything that reaches it is `PUBLIC`, and the consumer set is not declared closed; when it is declared closed, every stored crate's coverage is checked | +| `TargetsNotExtracted` / `TargetsUndeclared` | declared per crate (the store does not record targets) | +| `RegionsExcluded` | declared: opaque modules, features left off | +| `UnresolvedDispatch` | `UNRESOLVED_CALL` rows that may reach the symbol | +| `HarvestIncomplete` | residuals file, CRATE status | +| `StaleSource` / `FilesUnrecorded` / `FreshnessUnchecked` | `.files.tsv` digests against the source root | +| `SourceFilesUnread` | `.rs` files below a directory the extraction read from, absent from `files.tsv` | + +A missing symbol in a stored crate carries its crate's target, region and file gaps, because each +is a place it may still be defined. `closed_in_scope()` is true only for a found symbol with no +gap, and it claims nothing about code outside the scope. `Coverage::no_known_gap()` alone is not +treated as closure: test 2 asserts it is true while the report is not closed. + +`tests/impact_coverage.rs`, 14 tests: one per case in the brief, three found while running +Gate B (excluded regions, target gaps of a missing symbol, unread files), and three from review +(a private symbol behind a public caller, a closed consumer set checks every stored crate, a +missing symbol's harvest gap). Every guard was disabled +and its test went red: external consumers, missing targets, undeclared targets, dispatch fire, +dispatch silence, stale source, owner stored, observed consumers, closed symbol, harvest, +missing-symbol scope gaps, excluded regions, unread files (fire and silence), missing-symbol file +gaps, public frontier, closed consumer set, missing-symbol harvest. + +## 2. The report-rs store (Gate B) + +Inputs: report-rs `81705d9` (#33 head). Charon `c69e231`, nightly-2026-09-17, library target, +default features, `--opaque 'crate::app'` (the full crate ran out of memory at 15 GB). ruff_charon +from ruff `9752608`. + +| step | time (µs, reported, not gates) | result | +|---|---|---| +| charon | 42,192,655 | exit 0 | +| ruff_charon import | 25,202,579 | 29,542 harvested, 1 residual (`REFERENCE_NOT_NAMED view::Scope::only`) | +| six cg_query runs (cold open each) | 435,149 | | + +The CRATE row says `COMPLETE`, yet 14 of 59 files under `src/` were never read: `app.rs` and +`app/plan_routes.rs` (opaque), `iam.rs`, `login.rs`, `flow_scratch.rs`, `project_plan/*` (features +off), `main.rs` (binary target), `workbook_delta/dt64/p21_lane_socket_lab.rs`. That is what +`SourceFilesUnread` reports, from `files.tsv` alone. Nothing was inserted into the store by hand. + +Raw query output is in `store-output/`. + +### Q1: the two `WorkbookSource` types + +```mermaid +flowchart LR + load["Projection::load_v1"] -->|"CONFIRMED projection.rs:409"| new["source_fake::WorkbookSource::new"] + load -->|"CONFIRMED projection.rs:408"| lpull["Ledger::pull"] + into["source_graph::pull_into"] -->|"CONFIRMED source_graph.rs:357"| lpull + lpull -->|"UNKNOWN dyn Source, source.rs:333"| fpull["source_fake impl Source::pull"] + fpull -.->|IMPLEMENTS| spull["Source::pull"] + lpull -->|"CONFIRMED source.rs:334"| ingest["Ledger::ingest"] + poll["workbook_delta WorkbookSource::poll"] -.-|"0 callers in lib; tests, examples, app, main unread"| gap(("UNOBSERVED")) +``` + +- Confirmed: two identities, `report_rs::source_fake::WorkbookSource` and + `report_rs::workbook_delta::source::WorkbookSource`; only the first implements `Source`. + `Ledger::pull` calls `Ledger::ingest` (source.rs:334). `Projection::load_v1` builds the + `source_fake` source (projection.rs:409) and passes it to `Ledger::pull`. +- Possible / unknown: `Ledger::pull` reaches `source_fake::pull` only through `&mut dyn Source` + (source.rs:333). The compiler does not name that target, so the store reports UNKNOWN, not an edge. +- Absent in scope: `poll` has no caller in the library target with default features, and no path + from it reaches `Ledger::ingest`. Gaps: tests and examples not extracted, `crate::app` opaque, + `main.rs` unread, the symbol is public. +- Falsifier: one `src/` call of `WorkbookSource::poll` whose result reaches `Ledger::ingest` + (for example an `impl Source` for the delta type) turns this into a confirmed edge on re-import. + +### Q2: authorization + +`lance_graph_rbac` symbols are missing with `owner_stored: false`; `report_rs::iam::*` is missing +with `SourceFilesUnread` naming `src/iam.rs`. The store therefore cannot say whether report-rs +authorizes: the module that would do it was not compiled. Falsifier: re-extract with +`--features iam` and query `iam` callers. + +### Q3: map persistence + +No `lance_graph_dt64_lance` symbol in scope. `IdentityFrame::commit` calls +`lance_graph_dt64::dr::MapTable::advance` (frame.rs:195), CONFIRMED. Whether report-rs depends on +`lance-graph-dt64-lance` is a manifest fact the store does not hold; the store answer is +"absent in scope". Falsifier: a `MapLog` call in any compiled file. + +## 3. Graded comparison (Gate D) + +Two blind arms on the same model, the same questions (`questions.md`) and the same revision. +Arm M read source only. Arm C queried the store with `cg_query` and read source only at lines the +store printed. Grading against `answer-key.md`, strict: a materially false statement is WRONG even +with a coverage disclaimer. Full grades in `GRADES.tsv`; answers in `answers/`. + +| | correct | partial | unsupported | wrong | tool calls | elapsed s | tokens | +|---|---|---|---|---|---|---|---| +| M (source) | 12 | 0 | 0 | 0 | 13 | 71.0 | 456,784 | +| C (store) | 7 | 3 | 2 | 0 | 8 | 59.8 | 456,309 | + +The token counts are dominated by the shared session context both arms load, so they do not +separate the arms. n = 1 per arm; this is an observation, not a ranking. + +Where the store was weaker, and why: + +- **Build configuration.** Source text includes `#[cfg(feature = "iam")]` code; the store holds + one configuration. K2.1 (the `authorize_memberships` call) is visible only to arm M. +- **Targets.** The store holds the library target, so the test and example callers of `poll` + (K1.5) are unobserved. Arm C reported the gap; it did not name the callers. +- **Manifest facts.** "report-rs does not depend on dt64-lance" (K3.1, K3.3) comes from + Cargo.toml, which the store does not read. +- **Definition lines.** The store records function spans, not struct lines (K1.1). + +Where the store was stronger: arm C's `Ledger::pull → source_fake::pull` is reported UNKNOWN +(dynamic dispatch) instead of asserted, and every absence it states carries its scope. Arm M's +text search is feature-blind in both directions: it saw the `iam` call, and it would equally have +reported a cfg-dead call as live. + +Acceptance (brief): the store independently recovers the first #33 missing-integration finding, +explains it correctly, and reports what it cannot prove. Met. + +## 4. #2000 checks + +- The PR description, REPORT.md §4 and `recon.py --self-test` all give 15 tests; the self-test + passes. +- CI at the head (`90fc5a7`) and the merge (`317c4ff`) is green on every workflow. +- Reproduction from the documented commands, in a fresh worktree at `317c4ff` with sibling ndarray + at `fd3d2e7`: rows per crate equal REPORT.md's table (represented + 3), `machine-recovered.mmd` + is byte-identical, and the Charon column of `score.tsv` is identical (61 HIT, 26 MISS, + 32 UNOBSERVED). The rust-analyzer column was not re-run. + +## 5. Open + +- Feature and target matrices: one store per configuration, merged, would close K1.5 and K2.1. + Charon needs `crate::app` opaque at 15 GB. +- `RegionsExcluded` and `Targets` are declared by the caller. The extractor could record both in + the store (they are its own arguments), which would turn them into store facts. +- `SourceFilesUnread` scans the top directory of each recorded path; files recorded at the source + root trigger no scan. diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/answer-key.md b/.claude/probes/2026-11-11-codegraph-recon-2/answer-key.md new file mode 100644 index 000000000..3a86f7b21 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/answer-key.md @@ -0,0 +1,45 @@ +# Held-out answer key — D-CODEGRAPH-RECON-2 Gate B/D + +Source: EXO-185 manual findings (2026-11-10/11), re-checked by hand against +report-rs `81705d9` (PR #33 head). This file is NOT an input to any store and +is never shown to an arm before grading. + +## Q1 — the two `WorkbookSource` types (EXO-185 loose end A) + +K1.1 Two distinct identities: + - `report_rs::source_fake::WorkbookSource` (src/source_fake.rs:19) + - `report_rs::workbook_delta::source::WorkbookSource` (src/workbook_delta/source.rs:210) +K1.2 Only `source_fake::WorkbookSource` implements `report_rs::source::Source` + (src/source_fake.rs:29); its `pull` builds an `IngestBatch` (source_fake.rs:39, :61). +K1.3 `workbook_delta::source::WorkbookSource` has an inherent `poll` + (workbook_delta/source.rs:292) returning `Poll` (`Unchanged` | `Changed(VerifiedSnapshot)`); + it has no `Source` impl and builds no `IngestBatch`. +K1.4 `Ledger::pull(&mut dyn Source, _)` (src/source.rs:331) calls `Source::pull` (dynamic) + and `Ledger::ingest` (source.rs:334). The lib's only `Ledger::pull` caller with a + workbook source is `projection.rs:409`, passing `source_fake::WorkbookSource::new`. +K1.5 Missing integration: no lib (`src/`) code calls `workbook_delta::...::WorkbookSource::poll`. + Its only caller is `tests/p20_workbook_delta.rs:304`, which feeds `Workbook::ingest` + (tests/p20:308), not the ledger. So the real SharePoint puller never reaches + `Ledger::ingest`. Absence scope: all of `src/` and `tests/` at 81705d9, default features. +K1.6 Correct framing: the edge `poll → IngestBatch/Ledger::ingest` is ABSENT within the + lib target; test callers exist (p20). Claiming "poll has no callers at all" is FALSE. + +## Q2 — reader scope (EXO-185 loose end B, ledger item 3) + +K2.1 report-rs calls `lance_graph_rbac::authorize::authorize_memberships` only from + `src/iam.rs:250`, and `iam` is behind feature `iam` (src/lib.rs:22-23; Cargo.toml:107), + OFF by default. +K2.2 No call to `authorize_scoped` anywhere in report-rs `src/` or `tests/` at 81705d9. +K2.3 Correct framing for a default-features extraction: the `iam` module is UNOBSERVED + (not compiled), so "report-rs never authorizes" is FALSE; "no `authorize_scoped` call" + needs either the iam feature extracted or a source scope check. + +## Q3 — MapLog / value persistence (EXO-185 loose end C, ledger 5a/5b) + +K3.1 report-rs does not depend on `lance-graph-dt64-lance` (Cargo.toml: only + `lance-graph-dt64`, line 46); no `MapLog` use in src/ or tests/. +K3.2 DR identity lives in memory: `IdentityFrame` holds a `MapTable` + (src/workbook_delta/frame.rs:56), advanced in `IdentityFrame::commit` + (frame.rs:189, `self.table.advance(` at :195). +K3.3 Correct framing: the store cannot see crates report-rs does not depend on; "no + MapLog call" is a dependency-scope fact (Cargo.toml), not a call-graph absence. diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/answers/armC.md b/.claude/probes/2026-11-11-codegraph-recon-2/answers/armC.md new file mode 100644 index 000000000..0bfac3131 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/answers/armC.md @@ -0,0 +1,29 @@ +# Arm C answers (Charon graph, report-rs 81705d9; scope S = lib target, default features, crate::app opaque) +Scope S, stated once: report_rs lib target only (tests/examples/benches NOT extracted); region crate::app opaque; features lance,iam,flow,oidc,projects,projects-pg off. Unread files per GAP: src/app.rs, src/app/plan_routes.rs, src/flow_scratch.rs, src/iam.rs, src/login.rs, src/main.rs, src/project_plan/*, src/workbook_delta/dt64/p21_lane_socket_lab.rs. External consumers of public items are UNOBSERVED. Harvest has 1 residual (HarvestIncomplete). + +## Q1 +a. Two types, identified by graph module paths: + - report_rs::source_fake::WorkbookSource (non-generic; constructor `source_fake::WorkbookSource::new`) + - report_rs::workbook_delta::source::WorkbookSource (generic over a transport T; has new/into_transport/token/meta/poll) + Defining file:line: UNKNOWN from the graph (it prints no def lines; I did not read source at unprinted locations). Files by module path: src/source_fake.rs and src/workbook_delta/source.rs. Witness inside the first: src/source_fake.rs:39 (the `fn pull` in the Source impl). + CONFIRMED (identities), UNKNOWN (def lines). +b. source_fake::WorkbookSource implements Source (impl_Source_for_WorkbookSource::{id,name,lanes,pull} + vtable shim; src/source_fake.rs:39). The workbook_delta WorkbookSource has NO impl_Source_for_* symbol in scope S: ABSENT-IN-SCOPE (scope S). +c. Edges (all CONFIRMED, scope S): + - Ledger::pull -> Ledger::ingest: src/source.rs:334 (`self.ingest(batch)` on Ok). + - Ledger::replay -> Ledger::ingest: src/source.rs:357. + - projection::record_artifact -> Ledger::ingest: src/projection.rs:845, :852. + - Projection::load_v1 -> Ledger::pull: src/projection.rs:408, :413, :416; load_v1 -> source_fake::WorkbookSource::new: src/projection.rs:409. + - source_graph::pull_into -> Ledger::pull: src/source_graph.rs:357; load_v1 -> pull_into: src/projection.rs:429. + - Source::pull for source_fake::WorkbookSource: only edge is its vtable method (src/source_fake.rs:39); callers are dynamic dispatch via Ledger::pull, reported UNKNOWN by the graph (trait-object call, not resolved to the impl). + - IngestBatch: constructed/consumed by pull (return type), ingest, status/last_watermark/values closures and rules::source_basis closures; no separate "IngestBatch edge" witnessed beyond those symbols. + - Separate, different ingest: workbook_delta::ingest::Workbook::ingest, which calls IdentityFrame::commit (src/workbook_delta/ingest.rs:264); IdentityFrame::new called from Workbook::open (src/workbook_delta/ingest.rs:117). This is NOT Ledger::ingest. + CONFIRMED. +d. workbook_delta WorkbookSource::poll has 0 callers in scope S (sites=0); its callees are meta/token/item_url/sha256_hex/status_class and ReqwestDrive::get (POSSIBLE). No edge from poll (or from WorkbookSource::new, 0 refs) to Ledger::ingest or Workbook::ingest; Workbook::ingest also has 0 callers in S. Answer: ABSENT-IN-SCOPE for library code in S. Cannot say absent overall: poll/new/Workbook::ingest are PUBLIC (external callers UNOBSERVED), tests/examples not extracted, src/app.rs (opaque) and src/main.rs unread, and the p21 lab file unread, so a caller there is UNOBSERVED. +e. Scopes: as per header S for every "no caller/no edge" statement (Q1b, Q1d). + +## Q2 +Graph has no symbol containing authoriz, rbac, memberships, or lance_graph_rbac in the extracted scope, so no call to authorize_memberships / authorize_scoped appears: ABSENT-IN-SCOPE under scope S. Only unrelated names found (Ledger::register_scoped, Projection::scoped). The most likely home, src/iam.rs, is unread and feature iam is off, so under the `iam` feature (or in src/app.rs, opaque) the answer is UNOBSERVED. Witnesses: none (absence). Build configuration with a CONFIRMED call: none observed. + +## Q3 +Part 1, persistence through lance-graph-dt64-lance / MapLog: no symbol matching dt64_lance, lance_graph_dt64_lance, or MapLog: ABSENT-IN-SCOPE (scope S; features lance and others off, tests/examples not extracted, p21 lab file unread). Whether it happens under feature `lance` is UNOBSERVED. +Part 2, where the MapTable is held/advanced (CONFIRMED, scope S): lance_graph_dt64::dr::MapTable is held in the identity frame, workbook_delta::frame::IdentityFrame: MapTable::new(0,0) at src/workbook_delta/frame.rs:69 (via IdentityFrame::new, also from Default at :62), seal at src/workbook_delta/frame.rs:101, advance at src/workbook_delta/frame.rs:195 inside IdentityFrame::commit. commit is called by Workbook::ingest (src/workbook_delta/ingest.rs:264); Workbook::open builds the frame (src/workbook_delta/ingest.rs:117). The in-memory table is not persisted by any edge in S. Callers of Workbook::ingest: none in S (see Q1d), so the advance path is reached from library code only if an unobserved caller exists. diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/answers/armM.md b/.claude/probes/2026-11-11-codegraph-recon-2/answers/armM.md new file mode 100644 index 000000000..600356ce3 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/answers/armM.md @@ -0,0 +1,36 @@ +# Arm M answers (report-rs @ /home/user/rr-81705d9) + +Scope notes: default features = `default = []` (Cargo.toml `[features]`). Searched all `*.rs` under src/, tests/, examples/ (and Cargo.toml, Cargo.lock, Dockerfiles, docs/*.md for Q2/Q3). Only lib crate (src/lib.rs) and bin src/main.rs exist (no src/bin/); no workspace members. + +## Q1 +a. Two `WorkbookSource` types: + - `report_rs::source_fake::WorkbookSource` — src/source_fake.rs:19 (`pub struct WorkbookSource { fixture: Fixture }`). + - `report_rs::workbook_delta::source::WorkbookSource` — src/workbook_delta/source.rs:210 (`pub struct WorkbookSource { cfg, transport }`). Module declared at src/lib.rs:48 (`pub mod workbook_delta;`), `pub mod source;` in src/workbook_delta/mod.rs. + CONFIRMED. + +b. Only `source_fake::WorkbookSource` implements `report_rs::source::Source`: src/source_fake.rs:29 `impl Source for WorkbookSource`. The trait is src/source.rs:171. The workbook_delta type has only inherent impls (src/workbook_delta/source.rs:215 `impl WorkbookSource`); grep of "Source for" / "IngestBatch" / "Ledger" across src/workbook_delta/** found no `Source` trait impl (only an unrelated `LaneSource` in src/workbook_delta/dt64/p21_lane_socket_lab.rs:188 and dt64.rs). CONFIRMED (the delta type is not a `Source`); scope: src/workbook_delta/*.rs, src/workbook_delta/dt64/*.rs, all of src/. + +c. Edges: + - `Source::pull` declared src/source.rs:180 (`fn pull(&mut self, since: Option, now_ms: i64) -> Result`); returns `IngestBatch` (struct src/source.rs:135). Implemented at src/source_fake.rs:39 (workbook fake; builds `IngestBatch{..}`), 107, 188 (other fakes), src/source_graph.rs:257. + - `Ledger` struct src/source.rs:259; `Ledger::ingest(&mut self, batch: IngestBatch)` src/source.rs:302. + - `Ledger::pull(&mut self, src: &mut dyn Source, now_ms)` src/source.rs:330 calls `src.pull(since, now_ms)` (333) and on Ok `self.ingest(batch)` (334). This is the pull->IngestBatch->ingest edge. + - Callers of `Ledger::pull`: src/projection.rs:409 (`.pull(&mut WorkbookSource::new(base.clone()), now_ms)` using source_fake::WorkbookSource, import at projection.rs:10), 414, 417 (fake sharepoint/spear); src/source_graph.rs:357 (`ledger.pull(src, now_ms)` in `pull_into`); many tests (e.g. tests/p2_scope.rs:32). + - Other direct `Ledger::ingest` callers: src/projection.rs:845, 852 (mapping state batches); src/source.rs:357. (src/gov/mod.rs:48 / src/gov/observation.rs:263 are a different `ingest` on a different gov ledger type with `LicSnapshot`, not `source::Ledger`.) + - `poll`: only defined at src/workbook_delta/source.rs:292 (`WorkbookSource::poll(&mut self, last_etag, now_ms) -> Result`); it returns `Poll`, not `IngestBatch`. + - Delta-side landing is `workbook_delta::ingest::Workbook::ingest(&mut self, snap: &VerifiedSnapshot) -> Outcome` (src/workbook_delta/ingest.rs:161), a distinct method, not `Ledger::ingest`. + CONFIRMED. + +d. Does workbook_delta source reach `Ledger::ingest` from library code? No edge found. Callers of the delta `poll`: examples/workbook_sync.rs:37 (`src.poll(wb.last_etag(), now)`, then `wb.ingest(&snap)` at :46 which is `Workbook::ingest`), tests/p20_workbook_delta.rs:304,311,331,350. No call of `.poll(` anywhere in src/ outside its definition. Nothing in src/ outside src/workbook_delta references `workbook_delta` except `pub mod workbook_delta;` (src/lib.rs:48), and nothing in src/workbook_delta references `IngestBatch`/`source::Ledger`. So library code never calls delta `poll` and the delta source never reaches `source::Ledger::ingest`. ABSENT-IN-SCOPE. + +e. Scope for each "no edge / no caller": grep `-rn "poll("`, `"IngestBatch"` (files containing it: src/source_graph.rs, source_fake.rs, projection.rs, source.rs, tests/p3_evidence.rs, tests/p3b_delta.rs), `"workbook_delta"`, `"Workbook::open"` over all *.rs in src/, tests/, examples/ (src/main.rs, src/app.rs, src/app/** included); features: default (none) — and the grep is feature-agnostic (cfg-gated code is still text-searched, none of these hits are cfg-gated). Not examined: external crates (git deps), dynamic dispatch/macros beyond text grep. A grep with no hits is navigation only; I opened src/workbook_delta/mod.rs, source.rs (210-300), ingest.rs (1-125,150-165,260) to confirm the doc says the delta source "returns a verified snapshot" landed via `Workbook::ingest`. + +## Q2 +Yes, but only when feature `iam` is on. `authorize_memberships`: src/iam.rs:21 `use lance_graph_rbac::authorize::authorize_memberships;` and call at src/iam.rs:250 inside `LanceGraphScope::decide_in` (impl at src/iam.rs:243). `authorize_scoped`: no occurrence anywhere (grep of whole repo, *.rs/*.toml/*.md): ABSENT. +Build gating: `pub mod iam;` is `#[cfg(feature = "iam")]` (src/lib.rs:22-23); `lance-graph-rbac` is `optional = true` and enabled only via `iam = ["dep:lance-graph-rbac", ...]`; `lance = ["iam","oidc"]` (Cargo.toml [features]). Reach from app: src/scope.rs:563-566 `authority()` under `#[cfg(feature = "iam")]` constructs `crate::iam::LanceGraphScope::new` (src/scope.rs:566); `#[cfg(not(feature="iam"))]` arm (568) uses `StaticScope`. `authority` is called at src/app.rs:562, 752 etc. Dockerfile:36 builds `--features lance`; Dockerfile.standalone:18 has no rbac. +- authorize_memberships: CONFIRMED under `--features iam` (or `lance`); ABSENT in default-feature build (module not compiled). +- authorize_scoped: ABSENT-IN-SCOPE (all *.rs, Cargo.toml, docs, tests, examples searched; features irrelevant as no text hit). +Also tests/p33_intake.rs:263 only mentions lance_graph_rbac in a test name. + +## Q3 +No. There is no use of `lance-graph-dt64-lance` or `MapLog` anywhere: grep for "dt64-lance|dt64_lance|MapLog" over *.rs, *.toml, *.md, Dockerfiles, Cargo.lock found nothing (Cargo.toml dependencies list only `lance-graph-dt64` (git, main), not a `-lance` crate; Cargo.lock has `lance-graph-dt64` at line 1366 only). ABSENT-IN-SCOPE (all features; text-wide). +Identity `MapTable` (from `lance_graph_dt64::dr`) is held in memory in `IdentityFrame { table: MapTable, tiles }` (src/workbook_delta/frame.rs:55-57, constructed `MapTable::new(0, 0)` at :69) and advanced by `IdentityFrame::commit` calling `self.table.advance(source, 0, ...)` (src/workbook_delta/frame.rs:189-203). `IdentityFrame` is a field of `workbook_delta::ingest::Workbook` (src/workbook_delta/ingest.rs:99, built at :117 in `Workbook::open`); advance occurs at ingest.rs:264 `self.frame.commit(u64::from(number), fplan)`. Planning (`plan`, frame.rs:131) advances nothing. Persistence explicitly absent: src/workbook_delta/ingest.rs:11-16 ("Persistence is not here ... It writes no file and opens no dataset"). `Workbook::open` callers are only examples/ and tests/ (examples/workbook_sync.rs:26, workbook_head_to_head.rs:183, workbook_bench.rs:21, tests/p20_workbook_delta.rs:47,635,651); no src/ caller outside the module. CONFIRMED (in-memory only) / persistence ABSENT-IN-SCOPE. diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/questions.md b/.claude/probes/2026-11-11-codegraph-recon-2/questions.md new file mode 100644 index 000000000..3210137a5 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/questions.md @@ -0,0 +1,12 @@ +# Questions (report-rs at /home/user/rr-81705d9, commit 81705d9, default cargo features) + +Q1. The crate defines two types named `WorkbookSource`. + a. Give the fully qualified identity and defining file:line of each. + b. Which of them implements `report_rs::source::Source`? + c. Trace `poll`, `Source::pull`, `IngestBatch` and `Ledger::ingest`: which edges exist, with file:line witnesses? + d. Does the workbook_delta (SharePoint drive) source reach `Ledger::ingest` from library code? Who calls its `poll`? + e. For every "no edge / no caller" statement, state the scope you examined (files, targets, features). +Q2. Does report-rs call `lance_graph_rbac::authorize::authorize_memberships` or `authorize_scoped`? Under which build configuration? Give witnesses and scope. +Q3. Does report-rs persist DR identity through `lance-graph-dt64-lance` (`MapLog`)? Where is the identity `MapTable` held and advanced? Give witnesses and scope. + +Answer format: for each sub-question, a short statement, then CONFIRMED / ABSENT-IN-SCOPE / UNOBSERVED, then witnesses (file:line), then scope. Say UNKNOWN rather than guess. diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/fake_new.refs b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/fake_new.refs new file mode 100644 index 000000000..34e097722 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/fake_new.refs @@ -0,0 +1,2 @@ +ref CALLS_RESOLVED report_rs::projection::{Projection}::load_v1 src/projection.rs:409 +sites confirmed=1 possible=0 diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/identityframe_commit.callees b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/identityframe_commit.callees new file mode 100644 index 000000000..6443bc91b --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/identityframe_commit.callees @@ -0,0 +1,10 @@ +callees CONFIRMED alloc::vec::impl_Deref_for_Vec::deref +callees CONFIRMED alloc::vec::impl_IntoIterator_for_Vec::into_iter +callees CONFIRMED alloc::vec::{Vec[TraitClause0, TraitClause1, TraitClause2]}::push +callees CONFIRMED alloc::vec::{impl IntoIterator for &'a Vec[TraitClause0, TraitClause1, TraitClause2]}::into_iter +callees CONFIRMED core::iter::traits::iterator::Iterator::collect +callees CONFIRMED core::iter::traits::iterator::Iterator::map +callees CONFIRMED core::slice::iter::{impl Iterator for core::slice::iter::Iter<'a, T>[TraitClause0]}::next +callees CONFIRMED core::slice::{[T]}::contains +callees CONFIRMED lance_graph_dt64::dr::{MapTable}::advance +sites 9 diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callees b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callees new file mode 100644 index 000000000..45aa1a09e --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callees @@ -0,0 +1,7 @@ +callees CONFIRMED core::result::{Result[TraitClause0, TraitClause1]}::map +callees CONFIRMED report_rs::source::{report_rs::source::Ledger}::ingest +callees CONFIRMED report_rs::source::{report_rs::source::Ledger}::last_watermark +callees CONFIRMED report_rs::source::{report_rs::source::Ledger}::record_failure +callees CONFIRMED report_rs::source::{report_rs::source::Ledger}::register_scoped +callees UNKNOWN ?dynamic +sites 5 diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callers b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callers new file mode 100644 index 000000000..d3f09cfee --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.callers @@ -0,0 +1,4 @@ +callers CONFIRMED report_rs::projection::{Projection}::load_v1 +callers CONFIRMED report_rs::source_graph::pull_into +sites 4 +callers UNKNOWN external-callers-unobserved report_rs::source::{report_rs::source::Ledger}::pull is PUBLIC diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.refs b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.refs new file mode 100644 index 000000000..6273aa05d --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/ledger_pull.refs @@ -0,0 +1,5 @@ +ref CALLS_RESOLVED report_rs::source_graph::pull_into src/source_graph.rs:357 +ref CALLS_RESOLVED report_rs::projection::{Projection}::load_v1 src/projection.rs:408 +ref CALLS_RESOLVED report_rs::projection::{Projection}::load_v1 src/projection.rs:413 +ref CALLS_RESOLVED report_rs::projection::{Projection}::load_v1 src/projection.rs:416 +sites confirmed=4 possible=0 diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.authorize_memberships.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.authorize_memberships.report new file mode 100644 index 000000000..72e2a85f8 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.authorize_memberships.report @@ -0,0 +1,2 @@ +symbol MISSING owner_stored=false lance_graph_rbac::authorize::authorize_memberships +closed_in_scope false diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.maplog.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.maplog.report new file mode 100644 index 000000000..42f51924e --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.maplog.report @@ -0,0 +1,2 @@ +symbol MISSING owner_stored=false lance_graph_dt64_lance::MapLog +closed_in_scope false diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.report_rs_iam_authorize.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.report_rs_iam_authorize.report new file mode 100644 index 000000000..ed64b7a1b --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/missing.report_rs_iam_authorize.report @@ -0,0 +1,5 @@ +symbol MISSING owner_stored=true report_rs::iam::authorize +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.callers b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.callers new file mode 100644 index 000000000..7d615582b --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.callers @@ -0,0 +1,2 @@ +sites 0 +callers UNKNOWN external-callers-unobserved report_rs::workbook_delta::source::{report_rs::workbook_delta::source::WorkbookSource[TraitClause0, TraitClause1]}::poll is PUBLIC diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.report new file mode 100644 index 000000000..b0bc6a00f --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/poll.report @@ -0,0 +1,7 @@ +symbol FOUND 3615 report_rs::workbook_delta::source::{report_rs::workbook_delta::source::WorkbookSource[TraitClause0, TraitClause1]}::poll +GAP ExternalConsumersUnobserved { owner: "report_rs", observed_consumers: [] } +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.callers b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.callers new file mode 100644 index 000000000..72bd19dca --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.callers @@ -0,0 +1,10 @@ +callers CONFIRMED report_rs::source_fake::impl_Source_for_WorkbookSource::pull::{vtable_method} +callers UNKNOWN report_rs::mapping_hydrate::hydrate_artifact +callers UNKNOWN report_rs::mapping_hydrate::publish_artifact +callers UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl Fn<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call +callers UNKNOWN report_rs::mapping_hydrate::repoint +callers UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::fetch_manifest +callers UNKNOWN report_rs::rules::plan::{impl FnMut<(&'_2 Box<(dyn Rule + '_3), Global>[{built_in impl MetaSized for (dyn Rule + '_3)}, {built_in impl Sized for Global}, impl_Allocator_for_Global],)> for report_rs::rules::plan::{closure}<'_0, '_1>}::call_mut +callers UNKNOWN report_rs::source::{report_rs::source::Ledger}::pull +sites 1 +callers UNKNOWN external-callers-unobserved report_rs::source_fake::impl_Source_for_WorkbookSource::pull is PUBLIC diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.report new file mode 100644 index 000000000..dade573a9 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/pull.report @@ -0,0 +1,110 @@ +symbol FOUND 1717 report_rs::source_fake::impl_Source_for_WorkbookSource::pull +impact CONFIRMED report_rs::source_fake::impl_Source_for_WorkbookSource::pull::{vtable_method} +impact UNKNOWN report_rs::cli::run +impact UNKNOWN report_rs::cli::run::{impl FnOnce<(S3Client,)> for report_rs::cli::run::{closure#3}<'_0>}::call_once +impact UNKNOWN report_rs::cli::store +impact UNKNOWN report_rs::fixture::{ReportType}::within::{impl FnMut<(&'_1 &'_2 str,)> for report_rs::fixture::{ReportType}::within::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::fixture::{ReportType}::within::{impl FnOnce<(&'_1 &'_2 str,)> for report_rs::fixture::{ReportType}::within::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::lance_adapter::parity +impact UNKNOWN report_rs::mapping_hydrate::hydrate +impact UNKNOWN report_rs::mapping_hydrate::hydrate_artifact +impact UNKNOWN report_rs::mapping_hydrate::hydrate_artifact::{impl FnOnce<(Pointer,)> for report_rs::mapping_hydrate::hydrate_artifact::{closure}<'_0, '_1, T>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::hydrate_from_env +impact UNKNOWN report_rs::mapping_hydrate::hydrate_kind_from_env +impact UNKNOWN report_rs::mapping_hydrate::publish +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl Fn<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl FnMut<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl FnOnce<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call_once +impact UNKNOWN report_rs::mapping_hydrate::repoint +impact UNKNOWN report_rs::mapping_hydrate::store_from_env +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::fetch_manifest +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::load_local +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl Fn<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl FnMut<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl FnOnce<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl Fn<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnMut<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnOnce<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnOnce<()> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure#1}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::mobile::render +impact UNKNOWN report_rs::objstore::{S3Config}::from_suffixed_env +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl Fn<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl FnMut<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl FnOnce<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::oracle::summary +impact UNKNOWN report_rs::oracle::summary_where +impact UNKNOWN report_rs::oracle::summary_where::{closure}::{impl FnMut<(&'_1 &'_2 report_rs::fixture::Customer,)> for report_rs::oracle::summary_where::{closure}::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::oracle::summary_where::{closure}::{impl FnOnce<(&'_1 &'_2 report_rs::fixture::Customer,)> for report_rs::oracle::summary_where::{closure}::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::projection::labels_for +impact UNKNOWN report_rs::projection::{Projection}::load_v1 +impact UNKNOWN report_rs::projection::{Projection}::parity +impact UNKNOWN report_rs::projection::{Projection}::summary +impact UNKNOWN report_rs::rules::each_owed +impact UNKNOWN report_rs::rules::impl_Rule_for_ConfiguredButNoRun::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_ConfiguredButNoRun::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_DeliveredWithoutEvidence::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_DeliveredWithoutEvidence::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_EvidenceStale::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_EvidenceStale::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_OwedButNotConfigured::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_OwedButNotConfigured::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_RunFailed::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_RunFailed::propose::{vtable_method} +impact UNKNOWN report_rs::rules::plan::{impl FnMut<(&'_2 Box<(dyn Rule + '_3), Global>[{built_in impl MetaSized for (dyn Rule + '_3)}, {built_in impl Sized for Global}, impl_Allocator_for_Global],)> for report_rs::rules::plan::{closure}<'_0, '_1>}::call_mut +impact UNKNOWN report_rs::rules::plan::{impl FnOnce<(&'_2 Box<(dyn Rule + '_3), Global>[{built_in impl MetaSized for (dyn Rule + '_3)}, {built_in impl Sized for Global}, impl_Allocator_for_Global],)> for report_rs::rules::plan::{closure}<'_0, '_1>}::call_once +impact UNKNOWN report_rs::rules::validate::{impl FnMut<(&'_6 Change,)> for report_rs::rules::validate::{closure#1}<'_0, '_1, '_2, '_3, '_4, '_5, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::rules::validate::{impl FnOnce<(&'_6 Change,)> for report_rs::rules::validate::{closure#1}<'_0, '_1, '_2, '_3, '_4, '_5, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnMut<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#2}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnMut<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#3}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnOnce<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#2}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnOnce<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#3}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::{ActorSource}::from_env +impact UNKNOWN report_rs::scope::{ActorSource}::from_vars +impact UNKNOWN report_rs::scope::{StaticScope}::from_env +impact UNKNOWN report_rs::scope::{StaticScope}::from_vars +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::pull +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 (report_rs::source::SourceId, IngestBatchId), &'_2 mut IngestBatch)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 SourceFailure,)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#1}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut BTreeSet[{built_in impl Sized for ObservationLane}, {built_in impl Sized for Global}, impl_AllocatorClone_for_Global])> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#2}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut SourceScope)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#3}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut String)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#4}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 (report_rs::source::SourceId, IngestBatchId), &'_2 mut IngestBatch)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 SourceFailure,)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#1}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut BTreeSet[{built_in impl Sized for ObservationLane}, {built_in impl Sized for Global}, impl_AllocatorClone_for_Global])> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#2}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut SourceScope)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#3}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut String)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#4}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source_graph::pull_into +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_env +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl Fn<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl FnMut<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl FnOnce<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::source_graph::{GraphConfig}::lab_from_vars +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl Fn<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl FnMut<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl FnOnce<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_with +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::tools_html +impact UNKNOWN report_rs::view::project::{impl FnMut<(&'_1 report_rs::view::Column,)> for report_rs::view::project::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::view::project::{impl FnOnce<(&'_1 report_rs::view::Column,)> for report_rs::view::project::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::view::{Actor}::from_vars +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl Fn<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl FnMut<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl FnOnce<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::xlsx::export +impact UNKNOWN report_rs::xlsx::workbook +GAP ExternalConsumersUnobserved { owner: "report_rs", observed_consumers: [] } +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP UnresolvedDispatch { dynamic_calls: 17 } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-codegraph-recon-2/store-output/srcpull.report b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/srcpull.report new file mode 100644 index 000000000..145a5bdd9 --- /dev/null +++ b/.claude/probes/2026-11-11-codegraph-recon-2/store-output/srcpull.report @@ -0,0 +1,6 @@ +symbol FOUND 1718 report_rs::source::Source::pull +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-crai-report-1/REPORT.md b/.claude/probes/2026-11-11-crai-report-1/REPORT.md new file mode 100644 index 000000000..9f97beacf --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/REPORT.md @@ -0,0 +1,225 @@ +# D-CRAI-REPORT-1 and D-CODEGRAPH-RECON-2 (Gate A): Codegraph evidence as review reports + +**Status:** probe, advisory only. Board: EXO-229 (intake), EXO-230 (navigation). report-rs #33 / +EXO-185 (RECON-2 Gate B) moved to another session; this one covers #2000's follow-up, coverage-aware +impact, the report generator, and the ndarray #422 admission case. + +Nothing here posts PR comments, applies fixes, uploads anything or blocks CI. + +## 1. What landed + +| piece | where | what it does | +|---|---|---| +| coverage-aware impact | `crates/lance-graph-codegraph/src/impact_report.rs` | `Codegraph::impact_report(symbol, &ExtractionScope)` → `ImpactAnswer`: the unchanged `Codegraph::impact` mask plus typed `CoverageGap`s | +| CLI | `cg_query … impact-report SYMBOL [--deps a,b] [--with-tests] [--no-freshness]` | the same, for agents | +| report generator | `crai_report.py` | review input → `review.md` + `review.ndjson` | +| #422 evidence builder | `build_q8_evidence.py` | git + Cargo + Charon store → review input | +| examples | `examples/lance-graph-2000/`, `examples/ndarray-422/` | inputs and rendered reports | + +### Coverage-aware impact (RECON-2 Gate A) + +`Codegraph::impact` keeps its position-mask semantics (Decision 1). A caller in an unloaded crate has +no position, so it is reported as missing coverage and never as an entry in `Graded::unknown`. Facts the +rows cannot carry are declared by the caller in `ExtractionScope`: +- which targets were extracted; +- which crates depend on the symbol's crate; +- where the source is, for freshness. + +Anything the caller leaves undeclared becomes a gap. What the caller does declare is trusted: if a +real consumer is left out of the dependents list, the report cannot notice. + +| case | test (`tests/impact_coverage.rs`, on real `rev1` Charon facts) | disable run | +|---|---|---| +| no callers in a fully observed scope → no gap | `no_callers_inside_a_fully_observed_scope_has_no_gap` | — (silence twin) | +| public symbol, consumers not in store / not declared | `a_public_symbol_with_unobserved_consumers_reports_missing_coverage` | red (both gaps) | +| library-only extraction → test callers unobserved | `library_only_extraction_reports_unobserved_test_callers` | red | +| callers only through dispatch | `dispatch_only_callers_report_unresolved_dispatch` | red | +| changed source / freshness unchecked | `a_changed_source_file_reports_stale_coverage` | red (both) | +| symbol absent from the store | `a_symbol_absent_from_the_store_is_not_an_empty_impact` | red | +| consumer crate added: gap → CONFIRMED | `adding_the_consumer_crate_upgrades_missing_coverage_to_confirmed` | red | +| extraction residuals in a `COMPLETE` crate | `extraction_residuals_are_a_coverage_gap` | red | +| crate read without a residuals file | `a_missing_residuals_file_is_a_coverage_gap` | red | +| crate read without a CRATE row | `a_crate_without_a_crate_row_is_partial_not_absent` | red | +| symbol whose rows a refresh removed | `a_symbol_removed_by_a_refresh_is_not_found` | red | +| file-list row without a hash | `a_malformed_file_list_row_is_a_freshness_gap` | red | + +`ImpactReport::no_known_gap()` is documented as "no known gap within the declared scope", never as +completeness. That matches `diag::Coverage::no_known_gap`. + +One test premise was wrong and was corrected, not the code: `util::double` is not dispatch-free. It is +called by `Square::area`, which `area_of` reaches through dispatch, so `area_of` is a legitimate possible +impact of `double`. The silence twin uses `util::triple`. + +### The report generator (Gates 1 and 3) + +Records keep CodeRabbit's public `--agent` fields (`fileName`, `comment`, `severity`, `suggestions`, +`codegenInstructions`; crai-rs CR-SRC-01) and add crai-rs's five separate axes. + +**The generator computes, and never accepts from its input:** +- the evidence class: the highest grade among witnesses current at their own repository's head; +- the finding status (`stale` when a witness is old); +- whether the review is clean. + +`TEST_OBSERVED` needs a falsifier recorded as red under a disable run and then green. + +| Gate 3 control (and crai-rs id) | test | disable run | +|---|---|---| +| missing consumer crates → UNKNOWN, never "unaffected" | `test_missing_consumer_crates_yield_unknown_not_unaffected` | red | +| stale packet → no current-head finding (RS-F04) | `test_stale_packet_cannot_produce_a_current_head_finding` | red | +| evidence current only at its own repo's head | `test_cross_repo_evidence_is_current_only_at_its_own_head` | red | +| PARTIAL / SKIPPED / NOT_RUN / FAILED with 0 findings never clean (RS-F03) | `test_partial_or_skipped_with_zero_findings_is_never_clean` | red | +| named test without an executed falsifier stays NOT_RUN (RS-F05) | `test_a_named_test_without_an_executed_falsifier_stays_not_run` | red | +| compiler evidence vs reviewer hypothesis keep their grades; confidence never from severity (RS-F01/F02) | `test_compiler_evidence_and_reviewer_hypotheses_keep_separate_grades` | red | +| irrelevant benchmark does not become a defect | `test_an_irrelevant_benchmark_does_not_become_a_defect` | red | +| repeated input → byte-identical reports | `test_repeated_input_regenerates_byte_identical_reports` | red | +| POSSIBLE never a confirmed caller (RS-F07) | `test_possible_dispatch_is_never_a_confirmed_caller` | red | +| repetition is not a second witness (RS-F08) | `test_repetition_is_not_a_second_witness` | red | +| a stale finding blocks a clean verdict (RS-F04) | `test_a_stale_finding_blocks_a_clean_verdict` | red | +| a resolved record never absorbs an open one (RS-F08) | `test_a_resolved_record_does_not_absorb_an_open_one` | red | +| a different line span is a separate site (RS-F08) | `test_a_different_line_span_is_a_separate_site` | red | +| a run on another head is named in the verdict (RS-F04) | `test_a_run_on_another_head_is_named_in_the_verdict` | red | +| header fields are data, never structure (RS-F09) | `test_header_fields_are_data_never_structure` | red | +| agent text is quoted, never structure (RS-F09) | `test_agent_text_is_quoted_never_structure` | red | +| withdrawn keeps its record (RS-F10) | `test_withdrawn_keeps_its_record` | red | + +On real data: +- **#2000:** the packet regenerated at the merged head gives F1 `open`/COMPILER_DERIVED and F2 + `open`/SOURCE_STRUCTURE. The packet committed from an older head gives both `stale`/ASSERTED, with no + coverage claim. +- **Reproducibility:** both examples regenerate byte-identical from their inputs. + +Placement: the generator lives beside the packet in lance-graph, not in crai-rs, although crai-rs owns +the review semantics (its §7 owner table). crai-rs declares itself CR-H0 "documentation and +preregistered falsification only … not a … checker, runtime, PR reviewer". Moving executable code there +changes that phase, so it is the owner's decision. + +## 2. ndarray #422 (Gate 2): does the quotient optimisation reach the Foundry consumer? + +**Generated finding, from tools only:** not in shipped code. +- **The change:** #422's Rust changes are confined to the `u64x64` example target + (`[[example]] name = "u64x64"`). No library Rust file changed, and no crate can depend on an example. +- **G1 (`max_quantities`, `network_capacity`):** reaches a single division, the scalar ceiling + `(q + per_staff - 1) / per_staff` in `cook_ops`. It is the threshold operand of a mask predicate and + names no lane. +- **G6 (`execute_can_cook`, `PreparedMethod::{prepare, run}`):** reaches no division. Its guard is built at + registration (`method::methods` → `can_cook_guard`), which compares stock against `d * q` (`method.rs:55`) and + has the same scalar ceiling division for staff (`method.rs:58`). +- **How the threshold is evaluated:** mask-risc `Pred::GeI32 { lane, t: i32 }` (`ir.rs:133`) is passed to + `ndarray::simd::ge_i32_to_mask` unchanged (`exec.rs:588`), which is a compare against the splatted + threshold. Read as source; mask-risc is not in the Charon store, and its other predicate kinds were not read. +- **Not covered:** G1's per-store min/argmin step is not shipped (EXO-227 P1b), so the finding is about + shipped code only. +- **The stock quotient `stock / d`:** appears only in the oracle (`oracle.rs:62`) and the materialised + null model (`arm_a.rs:75`). Both reference arms reach it through a closure + body that Charon links to its parent by name only, so the edge is graded POSSIBLE. +- **A narrower point the PR's own spec gets wrong:** "No quotient is evaluated on the executable path" + is too strong, because a scalar ceiling division builds the staff threshold. It should say "no + per-row stock quotient". + +How the evidence was made trustworthy: +- **Charon first translated the wrong target.** `foundry-parity-lab` has a library and a `bench` + binary, and `charon cargo` picked the binary. The library came through as opaque dependency items: + 47 `BODY_NOT_STRUCTURED` residuals and zero calls. Read naively, that store says "the consumer calls + nothing, so nothing is reached", which is the false negative this work exists to prevent. The + residuals flagged it. Re-run with `-- --lib`: 1,636 facts, 928 resolved calls, 0 residuals. +- **Divisions come from Charon's assertions.** Each integer division carries a `DivisionByZero` and an + `Overflow:Div` effect at its exact span, so "which divisions does this path reach" is a compiler + fact, not a text search. +- **The threshold check is mechanical.** The builder reads the `t:` operands inside `cook_ops`'s + definition span (SOURCE_STRUCTURE) and checks that each executable division sits in one that names + no lane. + +Grading against the held-out manual review: §5. + +## 3. Corrections to RECON-1 (#2000) + +- **r2il was not clean.** `lance_graph_r2il`'s CRATE row is `PARTIAL`: Charon reported an error on + `pub type Select<'s> = dyn Fn(…)`. The RECON-1 report and the EXO-229 comment said the four crates ran + clean with one residual. The residual count was right, and the crate status was not. `impact_report` + now reports it as `PartialCrate`. +- **The reproduction record is not on main.** `c7151a1` was pushed after #2000 merged at `90fc5a7`, so + it is carried on this branch. + +## 4. Gap matrix: what Codegraph still cannot prove + +| gap | effect on an answer | measured where | what would close it | +|---|---|---|---| +| crates not in the store | external callers have no position | dt64: 261/370 public symbols; `admit`: 13 consumers missing | one Charon artifact per consumer (EXO-229) | +| test targets, `#[cfg(test)]` modules, examples | test callers unobserved | `LaneBinding::check`: 48 name-matched test sites, none resolved | Charon/RA on test targets | +| calls inside closures passed to std adapters | parent→closure edge missing | foundry-parity-lab: 96 closure bodies in the crate, linked only by name | link by Charon's naming as POSSIBLE (done in the #422 builder, not in Codegraph) | +| macro arguments (RA) | call rows missing; region marked | `lane_socket.rs`: 7/7 sites | Charon (LLBC sees through macros) | +| wrong Cargo target extracted | an apparently empty crate | foundry-parity-lab picked `bench` | name the target; treat `BODY_NOT_STRUCTURED` as refusal | +| Charon errors inside a crate | `PARTIAL` CRATE, zero residuals | r2il (`dyn Fn` type alias) | read CRATE status, not only residuals | +| declared scope is trusted | a wrong dependents list removes a real gap | my own wrong-column `--deps` gave "no consumer gap" | dependents from Cargo metadata, not a caller-built list | +| store location-dependence | identical content, different digests | contract build-script outputs, LLBC path | relative `OUT_DIR` paths in ruff_charon | +| semantics of a body | no BODY_SEMANTICS beyond R2IL bindings | Rust functions are `NOT_A_METHOD` | out of scope | +| production data | no recipe distribution | #422: PRODUCTION_DISTRIBUTION = UNKNOWN | not a code fact | +| performance | no timing evidence | #422 benchmarks: ASSERTED | executed benchmark with disable arms | + +## 5. Grading (Gate 4) + +**There was no held-out manual review to grade against.** No manual #422 review was found on the Linear +board, and the GitHub PR conversation was not reachable from this session. So recall against a human +review is **not measured**. What was measured instead: an independent grader, given only the rendered +`review.md`, the sources and the PR, checked every claim and every witness. Its claims were then checked +against source before any correction was made, since a grader's statements are claims too. + +**First version, as graded:** + +| measure | result | +|---|---| +| claims | 45: 41 correct, 3 unsupported, 1 wrong | +| witnesses | 14: 8 resolve, 5 partially, 1 not | +| unsupported | "no per-row quotient on G1/G6" stated without qualification while mask-risc was unknown; "G6 compares stock against `d*q`" with no witness for it | +| wrong | Q3's falsifier: `cargo run --release --example u64x64` only prints usage; the benchmark is built per tier and run as `u64x64 bench `, and F7/F8 are parity disable arms for `check`, not speed | +| witness defects | the store revision was given as `317c4ff` but the store was extracted at `c7151a1`; Q3 pointed at the example directory, not plan §17 | +| misses | the G6 ceiling division at registration (`method.rs:58`); G1's open min/argmin step (EXO-227); "56 closure bodies" was crate-wide, not per path | +| readability | "NOT CLEAN" read as "defects found"; Q1's hedge appeared only in the unknowns; Q2's suggestion named a lance-graph location for an ndarray plan | + +**Corrections, each verified in source first:** +- mask-risc `GeI32` evaluation added as SOURCE_STRUCTURE (`ir.rs:133`, `exec.rs:588`, ndarray + `simd_masking_ops.rs:3449`), and the unknown narrowed to the predicates that were not read; +- G6 registration reached from `method::methods`, with its `d * q` (`method.rs:55`) and ceiling division + (`method.rs:58`); +- witnesses pinned to the extraction revision `c7151a1`; the builder now refuses if + `foundry-parity-lab` or `lance-graph-mask-risc` differ between that revision and `--lg-head`; +- Q3's witness is plan §17 and its falsifier the plan's own per-tier `bench` invocation; +- Q1's claim is scoped to shipped code and names what it does not cover; +- the verdict reads `INCOMPLETE REVIEW: run PARTIAL; not a defect verdict`; +- Q2's suggestion names the ndarray plan file and line. + +**Found while correcting:** nested closures (a closure inside a closure, as in `methods`'s +`flat_map(|m| … map(move |q| …))`) were never linked to their owner, because Charon names the inner one +under `…::{closure}`, which is not itself a definition. That is why G6 registration was missed. Resolved +to the nearest named owner: 96 closure bodies linked, up from 56. + +**Accuracy after correction** has not been re-graded independently; the numbers above are for the first +version. Readability and actionability are judged by the grader, one reader; they are indications. + +**Smallest next verification** (from the corrected report): run Charon on `lance-graph-mask-risc` and +re-run the reach into it, for the predicate kinds other than `GeI32` that the consumer builds. + +**Tool cost:** + +| step | cost | +|---|---| +| Charon, foundry-parity-lab lib (deps built) | 2 s | +| ruff_charon import | ≤ 7 s | +| `build_q8_evidence.py` + `crai_report.py render` | < 1 s | +| independent grader | 51 tool calls, 533 s | + +## 6. Recommendation: advisory GitHub CI + +Not built in this slice. If you want it: + +1. A `workflow_dispatch`-only job, plus an opt-in label. It runs Charon on the crates a PR touches, + their direct dependents, and test targets. It renders `review.md` / `review.ndjson` as an artifact. +2. **No comment, no status check, no blocking.** The run status is printed in the summary, and + PARTIAL is the honest default. +3. **Refuse rather than report empty:** a crate with `BODY_NOT_STRUCTURED` residuals, a non-COMPLETE + CRATE row, or a stale `files.tsv` makes the job say `PARTIAL` with the reason, never `clean`. +4. **Cost** (measured here): Charon 2–29 s per crate (contract 21 s, dt64 29 s, foundry lib 2 s once + built); ruff_charon import ≤ 7 s; impact/report < 1 s. RA over the whole workspace takes 108 s and + should run only when macro-region coverage is wanted. +5. Promote to a PR comment only after a held-out precision measurement over several real PRs. One + graded case is an indication, not a calibration. diff --git a/.claude/probes/2026-11-11-crai-report-1/build_q8_evidence.py b/.claude/probes/2026-11-11-crai-report-1/build_q8_evidence.py new file mode 100644 index 000000000..b9bfe7262 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/build_q8_evidence.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python3 +"""Build the ndarray #422 admission-review input from tools, never from the PR's conclusions. + + python3 build_q8_evidence.py --ndarray DIR --base SHA --head SHA \ + --store FOUNDRY_STORE --lg-src LANCE_GRAPH_ROOT --lg-rev EXTRACTION_SHA [--lg-head SHA] > review-input.json + +Question: does the per-row quotient specialisation proposed in ndarray #422 reach the executable +Foundry consumer it names (lance-graph `foundry-parity-lab`, G1 capacity and G6 `CanCook`)? + +Facts come from: `git diff --name-only` and the PR head's `Cargo.toml` (what changed, in which +target kind); a Charon → ruff_charon store of `foundry-parity-lab`'s library target (calls, +dispatch, closures, and the division/overflow assertions Charon emits at each integer division). +The only human input is ENTRY_POINTS: the consumer functions the PR's spec names, labelled as such. +""" + +import argparse +import collections +import json +import re +import subprocess +import sys +from pathlib import Path + +# Named by the PR's spec (D-QUOT8-ADMISSION-0 §16, commit 3ef39dd), not derived here. +ENTRY_POINTS = { + "G1 capacity (arm_b)": ["arm_b::{ItemQuery<'s>}::max_quantities", "arm_b::{ItemQuery<'s>}::network_capacity"], + "G6 CanCook (method)": ["method::execute_can_cook", "method::{PreparedMethod<'s>}::prepare", + "method::{PreparedMethod<'s>}::run"], +} +# Where G6's methods are built: registration, not the execution entry points above. +REGISTRATION = ["method::methods"] +REFERENCE_ARMS = ["oracle::{Oracle}::network_capacity", "arm_a::{Objects}::network_capacity"] +CRATE = "foundry_parity_lab" +PLAN = ".claude/plans/u64x64-llvm-probe-v1.md" +DIV = ("DivisionByZero", "Overflow:Div") +NL = b"\n" + + +def fnv1a64(data): + h = 0xCBF29CE484222325 + for b in data: + h = ((h ^ b) * 0x100000001B3) & 0xFFFFFFFFFFFFFFFF + return f"{h:016x}" + + +def git(d, *a): + return subprocess.run(["git", "-C", d, *a], capture_output=True, text=True, check=True).stdout + + +def rows(path): + cols, out = None, [] + for ln in Path(path).read_text().splitlines(): + if ln.startswith("#schema "): + cols = ln[8:].split("\t") + elif ln and not ln.startswith("#"): + out.append(dict(zip(cols, ln.split("\t")))) + return out + + +def locate(text, needle): + """1-based line of the single occurrence of `needle` in `text`; refuses 0 or 2+ hits.""" + hits = [i + 1 for i, ln in enumerate(text.splitlines()) if needle in ln] + if len(hits) != 1: + sys.exit(f"expected exactly one line containing {needle!r}, found {hits}") + return hits[0] + + +def line_of(root, f, off): + return Path(root, f).read_bytes()[: int(off)].count(b"\n") + 1 + + +def main(argv): + ap = argparse.ArgumentParser() + for a in ("--ndarray", "--base", "--head", "--store", "--lg-src", "--lg-rev"): + ap.add_argument(a, required=True) + # --lg-rev is the revision the store was extracted at; --lg-head, when given, is a later + # lance-graph head the review is about. The two consumer crates must be unchanged between them. + ap.add_argument("--lg-head") + a = ap.parse_args(argv) + nd, lg_root = a.ndarray, Path(a.lg_src) + lg_src = lg_root / "crates/foundry-parity-lab" + risc = lg_root / "crates/lance-graph-mask-risc/src" + # The witnesses carry --lg-rev, so both inputs must be at it: the checkout (committed and + # uncommitted state of the two consumer crates), and the store, whose file list records the + # hash of every source file it was extracted from. + consumer = ["crates/foundry-parity-lab", "crates/lance-graph-mask-risc"] + if subprocess.run(["git", "-C", str(lg_root), "diff", "--quiet", a.lg_rev, "--", *consumer]).returncode != 0: + sys.exit(f"--lg-src differs from {a.lg_rev} in the consumer crates (other revision or local changes)") + # `git diff` does not see untracked files; a new source file would be invisible to it. + untracked = git(str(lg_root), "status", "--porcelain", "--untracked-files=all", "--", *consumer).strip() + if untracked: + sys.exit(f"--lg-src has uncommitted or untracked files in the consumer crates:\n{untracked}") + listed = rows(f"{a.store}/{CRATE}.files.tsv") + if not listed: + sys.exit("the store has no file list; its extraction revision cannot be checked") + for f in listed: + src = lg_src / f["path"] + if not src.is_file() or fnv1a64(src.read_bytes()) != f["fnv64"]: + sys.exit(f"the store was not extracted from this source: {f['path']} differs") + # The file list is hashed, but rows and effects are separate files and could come from another + # extraction. Bind them to the hashed sources by content: every row or effect with a span must + # name a listed file and fit inside it, a definition's span must contain its own name (compiler-generated items excepted), and a + # division effect's span must contain a division. + hashed = {f["path"]: (lg_src / f["path"]).read_bytes() for f in listed} + for kind, name in (("rows", f"{CRATE}.rows.tsv"), ("effects", f"{CRATE}.effects.tsv")): + for r in rows(f"{a.store}/{name}"): + if r.get("file") in (None, "-") or r.get("start") in (None, "-"): + continue + src = hashed.get(r["file"]) + if src is None: + # Build-script outputs are recorded by absolute path and are not hashed sources. + if Path(r["file"]).is_absolute(): + continue + sys.exit(f"the store's {kind} name a file its file list does not: {r['file']}") + lo, hi = int(r["start"]), int(r["end"]) + if not 0 <= lo <= hi <= len(src): + sys.exit(f"the store's {kind} do not fit {r['file']}: span {lo}..{hi} of {len(src)} bytes") + text = src[lo:hi] + # Compiler-generated items (derives, drop glue, closures) have no name in their span. + generated = "{" in r["subject"] or any(seg.startswith("impl_") for seg in r["subject"].split("::")) + if kind == "rows" and r["predicate"] == "DEFINES" and not generated: + leaf = r["subject"].rsplit("::", 1)[-1].encode() + if leaf not in text: + sys.exit(f"the store's rows do not match the source: {r['subject']} is not defined at {r['file']}:{lo}") + if kind == "effects" and r["kind"] == "DivisionByZero" and b"/" not in text: + sys.exit(f"the store's effects do not match the source: no division at {r['file']}:{lo}") + same_at_head = None + if a.lg_head: + same_at_head = subprocess.run(["git", "-C", str(lg_root), "diff", "--quiet", a.lg_rev, a.lg_head, "--", + "crates/foundry-parity-lab", "crates/lance-graph-mask-risc"]).returncode == 0 + if not same_at_head: + sys.exit("consumer crates changed between the extraction revision and --lg-head; re-extract") + + # 1. What #422 changed, and in which Cargo target. + changed = sorted(git(nd, "diff", "--name-only", a.base, a.head).split()) + cargo = git(nd, "show", f"{a.head}:Cargo.toml") + examples = {m.group(2): m.group(1) for m in re.finditer(r'\[\[example\]\]\s*name\s*=\s*"([^"]+)"\s*path\s*=\s*"([^"]+)"', cargo)} + example_dirs = {str(Path(p).parent) + "/": n for p, n in examples.items()} + rust = [p for p in changed if p.endswith(".rs")] + in_example = {p: next((n for d, n in example_dirs.items() if p.startswith(d)), None) for p in rust} + lib_rust = [p for p, n in in_example.items() if n is None and p.startswith("src/")] + other_rust = [p for p, n in in_example.items() if n is None and not p.startswith("src/")] + + plan = git(nd, "show", f"{a.head}:{PLAN}") + + # 2. The consumer's compiler facts. + R = rows(f"{a.store}/{CRATE}.rows.tsv") + E = rows(f"{a.store}/{CRATE}.effects.tsv") + residuals = [r for r in rows(f"{a.store}/{CRATE}.residuals.tsv")] + defs = {r["subject"] for r in R if r["predicate"] == "DEFINES"} + callees, may, impls = collections.defaultdict(set), collections.defaultdict(set), collections.defaultdict(set) + for r in R: + if r["predicate"] == "CALLS_RESOLVED": + callees[r["subject"]].add(r["object"]) + elif r["predicate"] == "MAY_DISPATCH": + may[r["subject"]].add(r["object"].split(" via ")[0]) + elif r["predicate"] == "IMPLEMENTS": + impls[r["object"]].add(r["subject"]) + owned = collections.defaultdict(set) # closure bodies Charon names under their parent + for d in defs: + m = re.match(r"^(.*?)::\{impl Fn(?:Mut|Once)?<.*\{closure", d) + if not m: + continue + parent = m.group(1) + while parent not in defs and parent.endswith("::{closure}"): # a nested closure: its named owner + parent = parent[: -len("::{closure}")] + if parent in defs: + owned[parent].add(d) + divs = collections.defaultdict(list) + for e in E: + if e["kind"] in DIV: + ln = line_of(lg_src, e["file"], e["start"]) + text = Path(lg_src, e["file"]).read_bytes()[int(e["start"]):int(e["end"])].decode() + site = (f"crates/foundry-parity-lab/{e['file']}:{ln}", text) + if site not in divs[e["subject"]]: # one site carries both a zero and an overflow assertion + divs[e["subject"]].append(site) + src_defs = {r["subject"]: r for r in R if r["predicate"] == "DEFINES"} + + def thresholds(sym): + """Mask-predicate threshold operands (`t: …`) inside `sym`'s source span, with whether + each names a lane. Source structure, read mechanically from the span Charon gave.""" + d = src_defs[sym] + body = Path(lg_src, d["file"]).read_bytes() + out = [] + span = body[int(d["start"]):int(d["end"])] + pats = (rb"\bt:\s*([^,\n]+),", rb"SetExpr::i32\([^,]+,\s*Cmp::\w+,\s*([^)\n]+)\)") + for pat in pats: + for m in re.finditer(pat, span): + line = body[: int(d["start"]) + m.start()].count(NL) + 1 + out.append((f"crates/foundry-parity-lab/{d['file']}:{line}", + m.group(1).decode().strip(), b"lane" in m.group(1))) + return sorted(out, key=lambda x: int(x[0].rsplit(":", 1)[1])) + + def reach(root): + # Visited per (symbol, possible?) so a dispatch or closure visit cannot hide a later + # resolved-call path. A symbol is possible-only when no definite path reaches it. + visited, stack = {(root, False)}, [(root, False)] + while stack: + x, p = stack.pop() + nxt = [(c, p) for c in callees.get(x, ())] + nxt += [(i, True) for t in may.get(x, ()) for i in impls.get(t, ())] + nxt += [(c, True) for c in owned.get(x, ())] + for state in nxt: + if state not in visited: + visited.add(state) + stack.append(state) + definite = {x for x, p in visited if not p} + seen = {x for x, _ in visited} + return seen, seen - definite + + def short(s): + return s.split("::", 1)[1].split("::{impl")[0] + + lg = "AdaWorldAPI/lance-graph" + w_comp = lambda st, wit, q=None: {"grade": "COMPILER_DERIVED", "qualifier": q, "statement": st, "witness": wit, + "repo": lg, "revision": a.lg_rev, "tool": "charon c69e231 + ruff_charon"} + w_src = lambda st, wit: {"grade": "SOURCE_STRUCTURE", "statement": st, "witness": wit, "repo": "AdaWorldAPI/ndarray", + "revision": a.head, "tool": "git"} + + ev, exec_divs, confirmed, possible, missing_entry = [], set(), [], [], [] + ev.append(w_src(f"#422 changes {len(changed)} files: {len(rust)} Rust, {sum(1 for n in in_example.values() if n)} in example target(s) " + f"{sorted({n for n in in_example.values() if n})}; library Rust files changed: {len(lib_rust)}; " + f"other Rust files changed: {len(other_rust)}", + f"{a.base[:12]}..{a.head[:12]}")) + for label, roots in ENTRY_POINTS.items(): + for root in roots: + full = f"{CRATE}::{root}" + if full not in defs: + ev.append(w_comp(f"{label}: `{root}` is not in the store", "-")) + missing_entry.append(f"{label}: {root}") + continue + seen, poss = reach(full) + hits = sorted(x for x in seen if x in divs) + confirmed.append(f"{label}: {short(full)}") + ext = sorted({x.split("::")[0] for x in seen if not x.startswith(CRATE)} - {"core", "alloc", "std"}) + ev.append(w_comp(f"{label}: `{short(full)}` reaches {len(seen)} functions ({len(poss)} only via dispatch or a closure body); " + f"divisions reached: {[short(h) for h in hits] or 'none'}; other crates reached: {ext}", "store")) + exec_divs.update(hits) + for s in sorted(exec_divs): + for wit, text in divs[s]: + ev.append(w_comp(f"integer division on an executable path, in `{short(s)}`: `{text}`", wit)) + threshold_rows = {s: thresholds(s) for s in sorted(exec_divs)} + lg_src_w = lambda st, wit: {"grade": "SOURCE_STRUCTURE", "statement": st, "witness": wit, "repo": lg, + "revision": a.lg_rev, "tool": "source span read"} + for s, ts in threshold_rows.items(): + for wit, t, names_lane in ts: + ev.append(lg_src_w(f"mask-predicate threshold in `{short(s)}`: `t: {t}` ({'names a lane' if names_lane else 'names no lane'})", wit)) + reg_divs = set() + for root in REGISTRATION: + full = f"{CRATE}::{root}" + if full not in defs: + sys.exit(f"registration root {root} is not in the store") + seen, _ = reach(full) + reg_divs.update(x for x in seen if x in divs and x not in exec_divs) + for s in sorted(reg_divs): + for wit, text in divs[s]: + ev.append(w_comp(f"integer division at G6 method registration (`{short(s)}`, reached from " + f"{[r for r in REGISTRATION]}, not from the G6 execution entry points): `{text}`", wit)) + for wit, t, names_lane in thresholds(s): + ev.append(lg_src_w(f"mask-predicate threshold in `{short(s)}`: `{t}` ({'names a lane' if names_lane else 'names no lane'})", wit)) + for root in REFERENCE_ARMS: + full = f"{CRATE}::{root}" + seen, poss = reach(full) + for h in sorted(x for x in seen if x in divs): + how = ("through a closure body linked to its parent by Charon's name only" if h in poss + else "through resolved calls") + for wit, text in divs[h]: + ev.append(w_comp(f"reference arm `{short(full)}` reaches `{short(h)}` {how}: `{text}`", wit, + "POSSIBLE" if h in poss else None)) + possible.append(f"{short(full)} → {short(h)}") + # How mask-risc evaluates the `GeI32` predicate the G1/G6 thresholds feed. Not in the Charon + # store, so read as source at the extraction revision; the kernel is read at #422's head. + ir, ex = (risc / "ir.rs").read_text(), (risc / "exec.rs").read_text() + kern = git(nd, "show", f"{a.head}:src/simd_masking_ops.rs") + risc_w = lambda st, f, needle: lg_src_w(st, f"crates/lance-graph-mask-risc/src/{f}:{locate(ir if f == 'ir.rs' else ex, needle)}") + ev.append(risc_w("mask-risc `Pred::GeI32 { lane: u16, t: i32 }` is `lane[i] >= t` with `t` a scalar `i32`", + "ir.rs", "GeI32 { lane: u16, t: i32 }")) + ev.append(risc_w("the executor passes `t` unchanged to `ndarray::simd::ge_i32_to_mask`", + "exec.rs", "(Pred::GeI32 { lane, t: v }, None) => ge_i32_to_mask")) + ev.append(w_src("`ge_i32_to_mask` is the complement of `lt_i32_to_mask`, a compare of each lane value against " + "the splatted threshold; no division", + f"src/simd_masking_ops.rs:{locate(kern, 'pub fn ge_i32_to_mask(')}")) + unknown = [{"what": f"entry point {m}", "why": "not in the store; its path was not examined"} for m in missing_entry] + unknown += [{"what": "lance-graph-mask-risc beyond `GeI32`", "why": "not in the Charon store; only the `GeI32` evaluation path was read, as source"}, + {"what": "G1 per-store min/argmin (limiting resource)", "why": "not shipped: open in EXO-227 (P1b); this review covers shipped code only"}, + {"what": "production recipe divisor distribution", "why": "no production source; the only population is the synthetic fixture"}, + {"what": f"{sum(len(v) for v in owned.values())} closure bodies in the crate", "why": "linked to their parent by Charon's naming only (POSSIBLE), not by a call row"}] + if residuals: + unknown.append({"what": f"{len(residuals)} residual facts", "why": "ruff_charon could not represent them"}) + + stock_quotient_on_exec = any("/ d" in t for s in exec_divs for _, t in divs[s]) + # Every executable division must sit in a threshold operand that names no lane. + def in_scalar_threshold(s, text): + """The division is a lane-free threshold operand, directly or through one `let NAME = `.""" + d = src_defs[s] + span = Path(lg_src, d["file"]).read_bytes()[int(d["start"]):int(d["end"])].decode() + m = re.search(r"let\s+(\w+)\s*=\s*" + re.escape(text), span) + names = {text} | ({m.group(1)} if m else set()) + return any(not lane and any(n in t for n in names) for _, t, lane in thresholds(s)) + div_in_scalar_threshold = all(in_scalar_threshold(s, text) for s in exec_divs for _, text in divs[s]) + reg_in_scalar_threshold = all(in_scalar_threshold(s, text) for s in reg_divs for _, text in divs[s]) + g6_compares_dq = any(t.replace(" ", "") == "d*q" and not lane for s in reg_divs for _, t, lane in thresholds(s)) + findings = [{ + "id": "Q1", "kind": "admission", + "location": {"file": "examples/u64x64/route_c.rs", "symbol": "u64x64 route C / A power-of-two quotient arms"}, + "claim": (("In the consumer's shipped code, the per-row power-of-two quotient specialisation in #422 has no " + "call site to reach. #422's Rust changes are confined to the `u64x64` example target, which no crate " + "can depend on. The G1 and G6 paths in the Charon store reach no per-row stock quotient: their " + "mask predicates compare a stock lane against a scalar threshold" + + (" (G6: `d * q`, built at method registration)" if g6_compares_dq else "") + + ", and mask-risc evaluates `GeI32` as a plain compare. Not covered: mask-risc predicates other " + "than `GeI32`, and G1's unshipped per-store min/argmin step (EXO-227 P1b).") + if not stock_quotient_on_exec and not lib_rust and not other_rust and not missing_entry else + "Not established: " + + "; ".join(x for x in ( + "a stock quotient is on an executable path" if stock_quotient_on_exec else "", + f"library Rust changed ({len(lib_rust)} files)" if lib_rust else "", + f"Rust changed outside the library and the example targets: {other_rust}" if other_rust else "", + f"entry points missing from the store, so their paths were not examined: {missing_entry}" if missing_entry else "", + ) if x) + ". See evidence."), + "evidence": ev, + "reachability": {"confirmed": confirmed, "possible": possible, "unknown": unknown}, + "coverage": {"consumer_store": f"{CRATE} library target", "consumer_residuals": len(residuals), + "ndarray_library_rust_changed": len(lib_rust), "ndarray_other_rust_outside_examples": len(other_rust)}, + "falsifier": {"description": ("Run Charon on lance-graph-mask-risc and re-run the reach from the G1/G6 entry points " + "into it: any `DivisionByZero` assertion on a path that takes a stock lane as an " + "operand refutes the claim. Smallest next verification: the predicate kinds other " + "than `GeI32` that the consumer builds."), + "status": "NOT_RUN"}, + "suggestion": {"refusal": "an admission finding, not a defect: no code change follows from the evidence"}, + }] + if exec_divs: + sites = [w for s in sorted(exec_divs) for w, _ in divs[s]] + th = [lg_src_w(f"`t: {t}` ({'names a lane' if lane else 'names no lane'})", wit) + for s in sorted(exec_divs) for wit, t, lane in threshold_rows[s] if "/" in t] + findings.append({ + "id": "Q2", "kind": "research", + "location": {"file": "crates/foundry-parity-lab/src/arm_b.rs", "symbol": "arm_b::{ItemQuery}::cook_ops"}, + "claim": (("The executable path does evaluate integer division: `(q + per_staff - 1) / per_staff`. It is the " + "threshold operand of a mask predicate and names no lane, so it is computed once when the predicate is " + "built, not per row. G6 has the same ceiling division at method registration" + + ("" if reg_in_scalar_threshold else " (not confirmed as a threshold operand)") + + ". In shipped code, a per-row quotient specialisation has nothing to apply to.") + if div_in_scalar_threshold else + "The executable path evaluates integer division outside a scalar threshold; see evidence."), + "evidence": [w_comp("division assertion (zero and overflow) at this site", w) for w in sites] + + [w_comp("division assertion at G6 method registration", w) for s in sorted(reg_divs) for w, _ in divs[s]] + + th, + "reachability": {"confirmed": [short(s) for s in sorted(exec_divs | reg_divs)]}, + "falsifier": {"description": "Read the Pred::GeI32 threshold sites: the division's operands are query parameters, not lanes.", + "status": "NOT_RUN"}, + "suggestion": {"file": f"{PLAN} (ndarray)", "line": locate(plan, "quotient is evaluated on the executable path"), + "text": ("Narrow 'No quotient is evaluated on the executable path' to 'no per-row stock " + "quotient is evaluated': a scalar ceiling division builds the staff threshold."), + "base_sha": a.head}, + }) + findings.append({ + "id": "Q3", "kind": "research", + "location": {"file": "examples/u64x64/main.rs"}, + "claim": "The PR's benchmark results (power-of-two shift no-regret; best route only on AVX-512 hot) were not re-run here.", + "evidence": [{"grade": "ASSERTED", "statement": "benchmark results and disable runs, author-reported (plan §17)", + "witness": f"{PLAN}:{locate(plan, '## 17. Results, D-QUOT8-ADMISSION-0')}", + "repo": "AdaWorldAPI/ndarray", "revision": a.head}], + "falsifier": {"description": ("Per tier T in v2/v3/v4, build with `env -u RUSTFLAGS cargo --config .cargo/config-$T.toml " + "rustc --release --example u64x64` (plan §4) and run `u64x64 bench ` for the " + "§16 scenarios; compare with §17. The F7/F8 disable arms (test `d > 0` instead of " + "`is_power_of_two`) are parity checks: they must turn `u64x64 check` red, and say " + "nothing about speed."), + "status": "NOT_RUN"}, + "suggestion": {"refusal": "a performance claim outside this review's evidence; no defect follows"}, + }) + review = {"review": {"repo": "AdaWorldAPI/ndarray", "pr": 422, "base_sha": a.base, "head_sha": a.head, + "heads": {lg: a.lg_rev}, + "scope": ("PR #422 commits; consumer: lance-graph foundry-parity-lab (lib), extracted at " + f"{a.lg_rev}" + (f"; foundry-parity-lab and mask-risc unchanged at {a.lg_head}" + if same_at_head else "")), + "reviewer": {"tool": "crai_report.py + build_q8_evidence.py"}}, + "run": {"status": "PARTIAL", "reasons": ["mask-risc not in the Charon store (GeI32 read as source only)", + "no test or benchmark executed"]}, + "findings": findings} + json.dump(review, sys.stdout, indent=1, sort_keys=True) + print() + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/.claude/probes/2026-11-11-crai-report-1/crai_report.py b/.claude/probes/2026-11-11-crai-report-1/crai_report.py new file mode 100644 index 000000000..54f3a046c --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/crai_report.py @@ -0,0 +1,450 @@ +#!/usr/bin/env python3 +"""D-CRAI-REPORT-1: render compiler evidence as a CodeRabbit-shaped review, read-only. + + render --input REVIEW.json --md OUT.md --ndjson OUT.ndjson + from-packet --packet PACKET.json --repo R --pr N --head SHA [--scope S] > REVIEW.json + --self-test + +The finding record keeps CodeRabbit's public `--agent` fields (`fileName`, `comment`, `severity`, +`suggestions`, `codegenInstructions`; CR-SRC-01) and adds crai-rs's five separate axes +(CR-H0-REVIEW-SEMANTICS §2): severity, evidence class, confidence, provenance, lifecycle. + +The renderer computes, never accepts: +- the evidence class: the highest grade among witnesses that are current. Severity, confidence + and repetition never raise it (RS-F01, RS-F08). TEST_OBSERVED needs a test that was run and + shown able to fail (a disable or mutation run red, then the code green; §5, RS-F05). +- the finding status: a witness from a revision other than the review head, or from a stale + file, makes the finding `stale` with no coverage claim (RS-F04). +- whether the review is clean: only a COMPLETE run on the head with zero open findings + (§3, RS-F03). + +It never posts, applies or uploads anything. A suggestion is printed as an inert proposal bound to +its base revision and span; reviewer and agent text is printed as quoted data (RS-F09). +""" + +import argparse +import json +import sys +import tempfile +import unittest +from pathlib import Path + +GRADES = ["ASSERTED", "SOURCE_STRUCTURE", "COMPILER_DERIVED", "TEST_OBSERVED"] +RUN_STATUSES = {"NOT_RUN", "SKIPPED", "PARTIAL", "COMPLETE", "FAILED"} +FINDING_STATUSES = {"open", "resolved", "outdated", "stale", "withdrawn"} +KINDS = {"defect", "impact", "coverage", "admission", "research"} +SEMANTICS = "crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35)" + + +class InputError(Exception): + pass + + +def heads_of(review): + """Head revision per repository: the PR's own repo plus any declared in `review.heads`. A + witness from a repository with no declared head can never be current.""" + r = review["review"] if "review" in review else review + return {r["repo"]: r["head_sha"], **(r.get("heads") or {})} + + +def witness_is_current(w, heads, repo): + want = heads.get(w.get("repo", repo)) + return want is not None and w.get("revision") == want and w.get("fresh", True) + + +def evidence_class(finding, heads, repo): + """Highest grade among current witnesses. A TEST_OBSERVED witness counts only when its + falsifier ran red under a disable/mutation and green on the code (§5).""" + best = 0 + for w in finding.get("evidence", []): + if not witness_is_current(w, heads, repo): + continue + g = w.get("grade") + if g not in GRADES: + raise InputError(f"{finding['id']}: unknown grade {g!r}") + if g == "TEST_OBSERVED" and finding.get("falsifier", {}).get("status") != "RAN_RED_THEN_GREEN": + g = "ASSERTED" # a statement about a test that did not run red-then-green is a claim + best = max(best, GRADES.index(g)) + return GRADES[best] + + +def finding_status(finding, heads, repo): + stated = finding.get("status", "open") + if stated not in FINDING_STATUSES: + raise InputError(f"{finding['id']}: unknown status {stated!r}") + if stated in ("withdrawn", "resolved", "outdated"): + return stated + ev = finding.get("evidence", []) + if any(not witness_is_current(w, heads, repo) for w in ev): + return "stale" + return stated + + +def normalise(review): + """Validate the input and derive every computed field. Deterministic: no clock, no + environment, stable ordering.""" + r = review["review"] + head, repo, heads = r["head_sha"], r["repo"], heads_of(review) + run = review["run"] + if run["status"] not in RUN_STATUSES: + raise InputError(f"unknown run status {run['status']!r}") + out = [] + seen_claims = {} + for f in sorted(review.get("findings", []), key=lambda f: f["id"]): + if f.get("kind") not in KINDS: + raise InputError(f"{f['id']}: unknown kind {f.get('kind')!r}") + g = dict(f) + g["evidence_class"] = evidence_class(f, heads, repo) + g["status"] = finding_status(f, heads, repo) + g["confidence"] = f.get("confidence") if (f.get("confidence") or {}).get("source") else {"value": "UNKNOWN", "source": None} + g["severity"] = f.get("severity") # verbatim reviewer label, or None; never computed + fs = f.get("falsifier") or {} + g["falsifier"] = {"description": fs.get("description", "NOT_SPECIFIED"), "command": fs.get("command"), + "status": fs.get("status", "NOT_RUN"), "disable_run": fs.get("disable_run")} + # A repeat is the same claim, at the same place, in the same lifecycle state. A resolved + # record never absorbs an open one elsewhere or in another state. + loc = f.get("location") or {} + claim = (f.get("claim") or "").strip().lower() + key = (claim, loc.get("file"), loc.get("symbol"), tuple(loc.get("lines") or ()), g["status"]) if claim else None + if key and key in seen_claims: + g["duplicate_of"] = seen_claims[key] # repetition is not a second witness (RS-F08) + elif key: + seen_claims[key] = f["id"] + reach = f.get("reachability") or {} + g["reachability"] = {"confirmed": sorted(reach.get("confirmed", [])), + "possible": sorted(reach.get("possible", [])), + "unknown": sorted(reach.get("unknown", []), key=json.dumps)} + out.append(g) + open_findings = [f for f in out if f["status"] == "open" and "duplicate_of" not in f] + # A stale finding was neither confirmed nor refuted at this head, so it blocks a clean verdict. + stale = [f for f in out if f["status"] == "stale" and "duplicate_of" not in f] + wrong_head = run.get("head_sha", head) != head + clean = run["status"] == "COMPLETE" and not open_findings and not stale and not wrong_head + counts = f"{len(open_findings)} open finding(s)" + (f", {len(stale)} stale, not revalidated at the head" if stale else "") + verdict = ("CLEAN: complete run on the head, zero open or stale findings" if clean + else f"INCOMPLETE REVIEW: run {run['status']}; not a defect verdict ({counts})" if run["status"] != "COMPLETE" + else f"INCOMPLETE REVIEW: the run was on {run['head_sha']}, not the review head ({counts})" if wrong_head + else counts) + return {"review": r, "run": run, "findings": out, "verdict": verdict, "clean": clean} + + +def site(f): + loc = f.get("location") or {} + lines = loc.get("lines") + return loc.get("file", "-") + (f":{lines[0]}-{lines[1]}" if lines else "") + + +def to_ndjson(n): + r, rows = n["review"], [] + rows.append({"type": "review_start", "repo": r["repo"], "pr": r.get("pr"), "base_sha": r.get("base_sha"), + "head_sha": r["head_sha"], "heads": heads_of(n), "scope": r.get("scope"), "reviewer": r.get("reviewer"), + "semantics": SEMANTICS}) + for f in n["findings"]: + sug = f.get("suggestion") or {} + rows.append({ + "type": "finding", "id": f["id"], "kind": f["kind"], + "fileName": (f.get("location") or {}).get("file"), "lines": (f.get("location") or {}).get("lines"), + "symbol": (f.get("location") or {}).get("symbol"), + "comment": f.get("claim"), "severity": f["severity"], + "suggestions": [sug] if sug.get("text") else [], + "codegenInstructions": None, + "evidence_class": f["evidence_class"], "confidence": f["confidence"], + "provenance": f.get("provenance"), "status": f["status"], + "evidence": f.get("evidence", []), "reachability": f["reachability"], + "coverage": f.get("coverage", {}), "falsifier": f["falsifier"], + "refusal": sug.get("refusal"), "duplicate_of": f.get("duplicate_of"), + "status_detail": f.get("status_detail"), + }) + rows.append({"type": "complete", "status": n["run"]["status"], "reasons": n["run"].get("reasons", []), + "findings": len(n["findings"]), "clean": n["clean"], "verdict": n["verdict"]}) + return "".join(json.dumps(x, sort_keys=True, ensure_ascii=False) + "\n" for x in rows) + + +def q(text): + """Untrusted text as a Markdown blockquote: never a heading, link target or instruction.""" + return "\n".join("> " + ln.replace("`", "'") for ln in str(text).splitlines()) or "> " + + +def inline(text): + """Untrusted text inside one Markdown line or table cell: one line, no code span, no cell + break. An embedded newline can therefore never start a heading or a list item.""" + return " ".join(str(text).split()).replace("`", "'").replace("|", "/") + + +def to_md(n): + r = n["review"] + L = [f"# Review: {r['repo']}" + (f" #{r['pr']}" if r.get("pr") else ""), "", + f"- **Head:** `{r['head_sha']}`" + (f" (base `{r['base_sha']}`)" if r.get("base_sha") else ""), + *[f"- **Evidence head, {k}:** `{v}`" for k, v in sorted(heads_of(n).items()) if k != r["repo"]], + f"- **Scope:** {inline(r.get('scope', 'not stated'))}", + f"- **Reviewer:** {inline(json.dumps(r.get('reviewer'), sort_keys=True))}", + f"- **Run:** `{n['run']['status']}`" + (" (" + "; ".join(inline(x) for x in n["run"].get("reasons", [])) + ")" if n["run"].get("reasons") else ""), + f"- **Verdict:** {inline(n['verdict'])}", f"- **Semantics:** {SEMANTICS}. Severity, evidence and confidence are separate axes.", ""] + if not n["findings"]: + L += ["No findings." + ("" if n["clean"] else " This is **not** a clean review: the run did not complete."), ""] + for f in n["findings"]: + L += [f"## {f['id']} · {f['kind']} · {f['status']}", "", + f"**Location:** `{site(f)}`" + (f" · `{(f.get('location') or {}).get('symbol')}`" if (f.get('location') or {}).get('symbol') else ""), "", + f"| severity | evidence class | confidence |", "|---|---|---|", + f"| {f['severity']['value'] + ' (' + f['severity']['source'] + ')' if f['severity'] else 'not assessed'} | {f['evidence_class']} | {f['confidence']['value']}" + + (f" ({f['confidence']['source']})" if f['confidence']['source'] else "") + " |", "", + "**Claim**", "", q(f.get("claim", "")), ""] + if f.get("duplicate_of"): + L += [f"Same claim as {f['duplicate_of']}; not counted as a second witness.", ""] + if f.get("evidence"): + L += ["**Evidence**", "", "| grade | qualifier | statement | witness | revision |", "|---|---|---|---|---|"] + for w in f["evidence"]: + cur = "" if witness_is_current(w, heads_of(n), r["repo"]) else " (not current)" + where = (w.get("repo") or r["repo"]).split("/")[-1] + L.append(f"| {w['grade']} | {w.get('qualifier') or ''} | {inline(w.get('statement', ''))} | `{inline(where)}:{inline(w.get('witness', '-'))}` | `{(w.get('revision') or '-')[:12]}`{cur} |") + L.append("") + rc = f["reachability"] + if any(rc.values()): + L += ["**Reachability**", "", f"- confirmed: {', '.join(f'`{x}`' for x in rc['confirmed']) or 'none observed'}", + f"- possible: {', '.join(f'`{x}`' for x in rc['possible']) or 'none observed'}", + "- unknown: " + ("; ".join(u.get("what", str(u)) + (f" ({u['why']})" if u.get("why") else "") for u in rc["unknown"]) or "none recorded"), ""] + cov = f.get("coverage") or {} + if cov: + L += ["**Coverage**", ""] + [f"- {k}: {v}" for k, v in sorted(cov.items())] + [""] + fs = f["falsifier"] + L += ["**Falsifier**", "", f"- {fs['description']}", f"- status: `{fs['status']}`" + + (f", command `{fs['command']}`" if fs.get("command") else "")] + if fs.get("disable_run"): + L.append(f"- disable run: {fs['disable_run']}") + L.append("") + sug = f.get("suggestion") or {} + if sug.get("text"): + L += ["**Suggestion** (inert proposal; applying it needs the owner's approval and revalidation" + f" against `{sug.get('base_sha', '?')[:12]}`)", "", q(sug["text"]), ""] + elif sug.get("refusal"): + L += [f"**No suggestion:** {sug['refusal']}", ""] + if f.get("status_detail"): + L += [f"**Status detail:** {f['status_detail']}", ""] + return "\n".join(L).rstrip("\n") + "\n" + + +# -------------------------------------------------------------------------------------------- +# Adapter: a recon.py ReviewEvidencePacket into review input. Facts only; no severity. + +def from_packet(p, repo, pr, head, scope): + ident, cs, aff = p["identity"], p["changed_source"], p["affected_symbols"] + src = ident["source_span"] + rev = ident["commit_sha"] + fresh = not cs["stale"] and not cs.get("dependency_evidence_stale", False) + prov = {"tool": "lance-graph recon.py packet over a Charon/ruff_charon store", "revision": rev, + "crate_instance": ident["crate_instance"], "features": ident["features"], + "target": ident["target_triple"], "rustc": ident["rustc_version"]} + loc = {"file": src["file"], "lines": src["lines"], "symbol": cs["subject"]} + wit = [{"grade": "COMPILER_DERIVED", "statement": f"`{e['caller']}` calls `{cs['subject']}`", "witness": s, + "tool": "charon", "revision": rev, "fresh": fresh and e.get("fresh", True)} + for e in p.get("witnessed_edges", []) for s in e["sites"]] + tests = [i for f in p["findings"] if f["id"] == "tests" for i in f["items"]] + findings = [{ + "id": "F1", "kind": "impact", "location": loc, "provenance": prov, + "claim": f"Changing `{cs['subject']}` affects its in-store callers. Callers outside the analysed crates are not observed.", + "evidence": wit, + "reachability": {"confirmed": aff["confirmed"], "possible": aff["possible"], "unknown": aff["unknown"]}, + "coverage": {"observed_crate": ident["crate_instance"], "unsupported_regions": len(p.get("unsupported_regions", []))}, + "falsifier": {"description": "Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`.", + "status": "NOT_RUN"}, + "suggestion": {"refusal": "impact is a fact, not a defect; nothing to change"}, + }, { + "id": "F2", "kind": "coverage", "location": loc, "provenance": prov, + "claim": f"{len(tests)} test call sites match `{cs['subject'].split('::')[-1]}` by name. None is resolved to this symbol, and none ran.", + "evidence": [{"grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "statement": f"name match, receiver unresolved ({i['ra']})", + "witness": i["site"], "tool": "source search", "revision": rev, "fresh": fresh} for i in tests], + "reachability": {"unknown": [{"what": "test callers", "why": "library targets only; macro regions and cfg(test) modules not indexed"}]}, + "falsifier": {"description": "Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out.", + "status": "NOT_RUN"}, + "suggestion": {"refusal": "coverage gap; the next step is extraction, not a code change"}, + }] + return {"review": {"repo": repo, "pr": pr, "head_sha": head, "base_sha": cs.get("base"), "scope": scope, + "reviewer": {"tool": "crai_report.py", "input": "ReviewEvidencePacket"}}, + "run": {"status": "PARTIAL", "reasons": ["library targets only", "consumer crates not in store", "no test executed"]}, + "findings": findings} + + +# -------------------------------------------------------------------------------------------- +# Negative controls (D-CRAI-REPORT-1 Gate 3; crai-rs RS-F01..F10 where they apply). + +HEAD = "a" * 40 +OLD = "b" * 40 + + +def base(findings, status="COMPLETE"): + return {"review": {"repo": "x/y", "pr": 1, "head_sha": HEAD, "scope": "PR"}, "run": {"status": status}, "findings": findings} + + +def fnd(fid="F1", **kw): + f = {"id": fid, "kind": "defect", "claim": "c", "location": {"file": "a.rs", "lines": [1, 2]}} + f.update(kw) + return f + + +def w(grade, rev=HEAD, fresh=True, **kw): + return {"grade": grade, "statement": "s", "witness": "a.rs:1", "revision": rev, "fresh": fresh, **kw} + + +class Negatives(unittest.TestCase): + def test_missing_consumer_crates_yield_unknown_not_unaffected(self): + p = json.loads(Path(__file__).with_name("packet-LaneBinding-check.json").read_text()) + n = normalise(from_packet(p, "x/y", 1, p["identity"]["commit_sha"], "PR")) + unk = n["findings"][0]["reachability"]["unknown"] + self.assertTrue(any("outside the store" in u["what"] for u in unk)) + self.assertNotIn("unaffected", to_md(n).lower()) + + def test_stale_packet_cannot_produce_a_current_head_finding(self): # RS-F04 + n = normalise(base([fnd(evidence=[w("COMPILER_DERIVED", rev=OLD)])])) + self.assertEqual(n["findings"][0]["status"], "stale") + self.assertEqual(n["findings"][0]["evidence_class"], "ASSERTED") + n = normalise(base([fnd(evidence=[w("COMPILER_DERIVED", fresh=False)])])) + self.assertEqual(n["findings"][0]["status"], "stale") + + def test_cross_repo_evidence_is_current_only_at_its_own_head(self): + other = {"grade": "COMPILER_DERIVED", "statement": "s", "witness": "b.rs:1", "repo": "x/z", "revision": OLD} + undeclared = normalise(base([fnd(evidence=[other])])) + self.assertEqual(undeclared["findings"][0]["status"], "stale") # no declared head for x/z + rv = base([fnd(evidence=[other])]) + rv["review"]["heads"] = {"x/z": OLD} + self.assertEqual(normalise(rv)["findings"][0]["status"], "open") + self.assertEqual(normalise(rv)["findings"][0]["evidence_class"], "COMPILER_DERIVED") + + def test_partial_or_skipped_with_zero_findings_is_never_clean(self): # RS-F03 + for st in ("PARTIAL", "SKIPPED", "NOT_RUN", "FAILED"): + n = normalise(base([], status=st)) + self.assertFalse(n["clean"], st) + self.assertIn("INCOMPLETE REVIEW", n["verdict"]) + self.assertNotIn("CLEAN:", n["verdict"]) + self.assertTrue(normalise(base([]))["clean"]) # twin: COMPLETE with zero findings is clean + + def test_a_named_test_without_an_executed_falsifier_stays_not_run(self): # RS-F05 + n = normalise(base([fnd(evidence=[w("TEST_OBSERVED")], falsifier={"description": "t", "command": "cargo test x"})])) + self.assertEqual(n["findings"][0]["falsifier"]["status"], "NOT_RUN") + # A statement about a test that did not run is a claim, not source structure. + self.assertEqual(n["findings"][0]["evidence_class"], "ASSERTED") + green_only = fnd(evidence=[w("TEST_OBSERVED")], falsifier={"description": "t", "status": "RAN_GREEN_ONLY"}) + self.assertNotEqual(normalise(base([green_only]))["findings"][0]["evidence_class"], "TEST_OBSERVED") + ok = fnd(evidence=[w("TEST_OBSERVED")], falsifier={"description": "t", "status": "RAN_RED_THEN_GREEN"}) + self.assertEqual(normalise(base([ok]))["findings"][0]["evidence_class"], "TEST_OBSERVED") + + def test_compiler_evidence_and_reviewer_hypotheses_keep_separate_grades(self): # RS-F01/F02 + hyp = fnd("F1", severity={"value": "critical", "source": "reviewer"}, evidence=[w("ASSERTED")]) + comp = fnd("F2", claim="d", evidence=[w("COMPILER_DERIVED")]) + a, b = normalise(base([hyp, comp]))["findings"] + self.assertEqual((a["evidence_class"], a["severity"]["value"]), ("ASSERTED", "critical")) + self.assertEqual((b["evidence_class"], b["severity"]), ("COMPILER_DERIVED", None)) + self.assertEqual(a["confidence"]["value"], "UNKNOWN") # never derived from severity + + def test_an_irrelevant_benchmark_does_not_become_a_defect(self): + # A benchmark result with no witness on changed code is research, and stays ASSERTED. + bench = fnd(kind="research", claim="route F halves O6 on AVX-512", evidence=[w("ASSERTED")], + location={"file": "examples/u64x64/main.rs"}) + n = normalise(base([bench])) + self.assertEqual((n["findings"][0]["kind"], n["findings"][0]["evidence_class"]), ("research", "ASSERTED")) + self.assertNotIn("defect", to_ndjson(n)) + + def test_repeated_input_regenerates_byte_identical_reports(self): + p = json.loads(Path(__file__).with_name("packet-LaneBinding-check.json").read_text()) + i = from_packet(p, "x/y", 1, p["identity"]["commit_sha"], "PR") + a, b = normalise(json.loads(json.dumps(i))), normalise(json.loads(json.dumps(i))) + self.assertEqual((to_md(a), to_ndjson(a)), (to_md(b), to_ndjson(b))) + shuffled = dict(i, findings=list(reversed(i["findings"]))) + self.assertEqual(to_ndjson(normalise(shuffled)), to_ndjson(a)) + + def test_possible_dispatch_is_never_a_confirmed_caller(self): # RS-F07 + n = normalise(base([fnd(reachability={"possible": ["c"], "confirmed": []})])) + self.assertEqual(n["findings"][0]["reachability"]["confirmed"], []) + + def test_repetition_is_not_a_second_witness(self): # RS-F08 + n = normalise(base([fnd("F1", claim="same"), fnd("F2", claim="Same")])) + self.assertEqual(n["findings"][1].get("duplicate_of"), "F1") + self.assertIn("1 open finding", n["verdict"]) + + def test_a_stale_finding_blocks_a_clean_verdict(self): # RS-F04 + n = normalise(base([fnd(evidence=[w("COMPILER_DERIVED", rev=OLD)])])) + self.assertEqual(n["findings"][0]["status"], "stale") + self.assertFalse(n["clean"]) + self.assertIn("1 stale", n["verdict"]) + + def test_a_resolved_record_does_not_absorb_an_open_one(self): # RS-F08 + n = normalise(base([fnd("F1", claim="same", status="resolved", evidence=[w("COMPILER_DERIVED")]), + fnd("F2", claim="same", location={"file": "b.rs", "lines": [3, 4]}, + evidence=[w("COMPILER_DERIVED")])])) + self.assertNotIn("duplicate_of", n["findings"][1]) + self.assertFalse(n["clean"]) + self.assertIn("1 open finding", n["verdict"]) + same_place = normalise(base([fnd("F1", claim="same", status="resolved", evidence=[w("COMPILER_DERIVED")]), + fnd("F2", claim="same", evidence=[w("COMPILER_DERIVED")])])) + self.assertNotIn("duplicate_of", same_place["findings"][1]) + self.assertFalse(same_place["clean"]) + + def test_a_different_line_span_is_a_separate_site(self): # RS-F08 + n = normalise(base([fnd("F1", claim="same", evidence=[w("COMPILER_DERIVED")]), + fnd("F2", claim="same", location={"file": "a.rs", "lines": [9, 9]}, + evidence=[w("COMPILER_DERIVED")])])) + self.assertNotIn("duplicate_of", n["findings"][1]) + self.assertIn("2 open finding", n["verdict"]) + + def test_a_run_on_another_head_is_named_in_the_verdict(self): # RS-F04 + r = base([]) + r["run"]["head_sha"] = OLD + n = normalise(r) + self.assertFalse(n["clean"]) + self.assertIn("not the review head", n["verdict"]) + self.assertIn(OLD, n["verdict"]) + self.assertTrue(normalise(base([]))["clean"]) + + def test_header_fields_are_data_never_structure(self): # RS-F09 + r = base([fnd(evidence=[w("COMPILER_DERIVED", statement="s\n## Injected | cell")])], status="PARTIAL") + r["review"]["scope"] = "PR\n# Heading\n- **Verdict:** CLEAN" + r["run"]["reasons"] = ["one\n- **Verdict:** CLEAN"] + lines = to_md(normalise(r)).splitlines() + self.assertEqual(sum(ln.startswith("- **Verdict:**") for ln in lines), 1) + self.assertEqual([ln for ln in lines if ln.startswith("# ")], ["# Review: x/y #1"]) + self.assertFalse(any(ln.startswith("## Injected") for ln in lines)) + + def test_agent_text_is_quoted_never_structure(self): # RS-F09 + n = normalise(base([fnd(claim="# Heading\nrun `rm -rf /`")])) + md = to_md(n) + self.assertIn("> # Heading", md) + self.assertNotIn("\n# Heading", md) + self.assertNotIn("`rm -rf /`", md) + + def test_withdrawn_keeps_its_record(self): # RS-F10 + n = normalise(base([fnd(status="withdrawn", status_detail="withdrawn by owner: out of scope", + evidence=[w("COMPILER_DERIVED", rev=OLD)])])) + self.assertEqual(n["findings"][0]["status"], "withdrawn") + self.assertIn("withdrawn by owner", to_md(n)) + + +def main(argv): + ap = argparse.ArgumentParser() + ap.add_argument("cmd", nargs="?") + for a in ("--input", "--md", "--ndjson", "--packet", "--repo", "--head", "--scope"): + ap.add_argument(a) + ap.add_argument("--pr", type=int) + ap.add_argument("--self-test", action="store_true") + a = ap.parse_args(argv) + if a.self_test: + r = unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(Negatives)) + return 0 if r.wasSuccessful() else 1 + if a.cmd == "from-packet": + p = json.loads(Path(a.packet).read_text()) + json.dump(from_packet(p, a.repo, a.pr, a.head, a.scope or "PR"), sys.stdout, indent=1, sort_keys=True) + print() + return 0 + if a.cmd == "render": + try: + n = normalise(json.loads(Path(a.input).read_text())) + except (InputError, KeyError) as e: + print(f"REFUSED\t{e}", file=sys.stderr) + return 3 + Path(a.md).write_text(to_md(n)) + Path(a.ndjson).write_text(to_ndjson(n)) + return 0 + ap.print_help() + return 2 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/packet.json b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/packet.json new file mode 100644 index 000000000..dc784df45 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/packet.json @@ -0,0 +1,553 @@ +{ + "packet": "ReviewEvidencePacket (prototype, read-only; not a vendor API)", + "identity": { + "repo_id": "AdaWorldAPI/lance-graph", + "commit_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "cargo_lock_sha": "NOT_TRACKED (repository tracks no Cargo.lock)", + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "target_triple": "x86_64-unknown-linux-gnu", + "rustc_version": "nightly-2026-09-17 (charon)", + "file_blob_sha": "a0aa6e6658872d07c93990095d2fab2beff1487d", + "source_span": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "bytes": [ + 10592, + 12497 + ], + "lines": [ + 307, + 349 + ] + } + }, + "changed_source": { + "subject": "lance_graph_dt64::lane::{LaneBinding}::check", + "base": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "diff": "NOT_SUPPLIED (prototype: change of the whole span)", + "stale": false, + "freshness": "FRESH", + "dependency_evidence_stale": false + }, + "affected_symbols": { + "confirmed": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "possible": [], + "stale": [], + "unknown": [ + { + "what": "callers in crates outside the store", + "observed_crates": [ + "lance_graph_contract", + "lance_graph_dt64", + "lance_graph_quack", + "lance_graph_r2il" + ], + "why": "symbol is PUBLIC; only these crates' library targets were compiled" + }, + { + "what": "test targets, examples, cfg(test) modules", + "why": "store holds library targets only" + } + ] + }, + "witnessed_edges": [ + { + "caller": "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "callee": "lance_graph_dt64::lane::{LaneBinding}::check", + "sites": [ + "crates/lance-graph-dt64/src/lane.rs:386" + ], + "fresh": true + }, + { + "caller": "lance_graph_dt64::lane::{LaneBinding}::lane", + "callee": "lance_graph_dt64::lane::{LaneBinding}::check", + "sites": [ + "crates/lance-graph-dt64/src/lane.rs:360" + ], + "fresh": true + } + ], + "possible_edges": [ + { + "dispatch_target": "lance_graph_dt64::lane::LaneSource::lane_of" + } + ], + "co_changed_only": "NOT_RUN (no history projection in this prototype)", + "unsupported_regions": [ + "crates/cognitive-polyfill-probe/tests/p0.rs", + "crates/dir-rmap-probe/tests/equivalence.rs", + "crates/dr-isa-stream-lab/tests/integration/g2l.rs", + "crates/dr-math-binding-probe/tests/binding.rs", + "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs", + "crates/lance-graph-dt64/tests/dr.rs", + "crates/lance-graph-dt64/tests/lane_socket.rs", + "crates/lance-graph-report/src/lane_source/tests.rs", + "crates/thought-git-probe/tests/receipt.rs", + "crates/xlog-relcomp-probe/tests/dto_binding.rs" + ], + "findings": [ + { + "id": "dependents", + "summary": "2 confirmed transitive in-store callers; external callers UNKNOWN", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "confidence": "as graded per item", + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "PARTIAL" + }, + { + "id": "tests", + "summary": "48 candidate test call sites by name; none run", + "severity": null, + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "items": [ + { + "site": "crates/cognitive-polyfill-probe/tests/p0.rs:271", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/cognitive-polyfill-probe/tests/p0.rs:275", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:234", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:238", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:249", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:481", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:522", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:663", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:677", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:678", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:680", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-math-binding-probe/tests/binding.rs:234", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:58", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:60", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:72", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:82", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:86", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:89", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:90", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:93", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:776", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:777", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:808", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:814", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:824", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:827", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:828", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:227", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:277", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:314", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:369", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:373", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:403", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:407", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:424", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/thought-git-probe/tests/receipt.rs:715", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/thought-git-probe/tests/receipt.rs:715", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:320", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:341", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:418", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:623", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + } + ], + "provenance": { + "tool": "source search + rust-analyzer GRAPH=1 (ra_ap 0.0.358)" + }, + "lifecycle": "open", + "run_status": "NOT_RUN" + }, + { + "id": "contracts", + "summary": "trait methods the symbol dispatches through (a change may interact with any impl)", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "qualifier": "POSSIBLE", + "items": [ + "lance_graph_dt64::lane::LaneSource::lane_of" + ], + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "COMPLETE" + }, + { + "id": "execution_paths", + "summary": "entry points (in-store callers with no in-store caller)", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "items": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "PARTIAL" + } + ], + "r2il": { + "method": "NOT_A_METHOD", + "body_semantics": "UNKNOWN", + "why": "R2IL describes the effects of a registered DT64 MethodBinding (prepare binds them to opcodes); a Rust function found by the compiler is not one, so its body has no R2IL reading" + }, + "expected_behavior": "NOT_SUPPLIED (must come from intent/tests, independently of this packet)", + "candidate_behavior": "NOT_INFERRED", + "missing_necessary_evidence": [ + "test runs (TEST_OBSERVED needs a run that can fail, CR-H0 \u00a75)", + "compiler artifacts of consumer crates and test targets", + "a resolved receiver for each name-matched test site" + ], + "permission_state": "NOT_DERIVED (never from confidence)" +} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input-stale.json b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input-stale.json new file mode 100644 index 000000000..72b7a8c8b --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input-stale.json @@ -0,0 +1,566 @@ +{ + "findings": [ + { + "claim": "Changing `lance_graph_dt64::lane::{LaneBinding}::check` affects its in-store callers. Callers outside the analysed crates are not observed.", + "coverage": { + "observed_crate": "lance_graph_dt64 (lib target)", + "unsupported_regions": 10 + }, + "evidence": [ + { + "fresh": true, + "grade": "COMPILER_DERIVED", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "`lance_graph_dt64::lane::{LaneBinding}::borrow_all` calls `lance_graph_dt64::lane::{LaneBinding}::check`", + "tool": "charon", + "witness": "crates/lance-graph-dt64/src/lane.rs:386" + }, + { + "fresh": true, + "grade": "COMPILER_DERIVED", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "`lance_graph_dt64::lane::{LaneBinding}::lane` calls `lance_graph_dt64::lane::{LaneBinding}::check`", + "tool": "charon", + "witness": "crates/lance-graph-dt64/src/lane.rs:360" + } + ], + "falsifier": { + "description": "Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`.", + "status": "NOT_RUN" + }, + "id": "F1", + "kind": "impact", + "location": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "lines": [ + 307, + 349 + ], + "symbol": "lance_graph_dt64::lane::{LaneBinding}::check" + }, + "provenance": { + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "rustc": "nightly-2026-09-17 (charon)", + "target": "x86_64-unknown-linux-gnu", + "tool": "lance-graph recon.py packet over a Charon/ruff_charon store" + }, + "reachability": { + "confirmed": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "possible": [], + "unknown": [ + { + "observed_crates": [ + "lance_graph_contract", + "lance_graph_dt64", + "lance_graph_quack", + "lance_graph_r2il" + ], + "what": "callers in crates outside the store", + "why": "symbol is PUBLIC; only these crates' library targets were compiled" + }, + { + "what": "test targets, examples, cfg(test) modules", + "why": "store holds library targets only" + } + ] + }, + "suggestion": { + "refusal": "impact is a fact, not a defect; nothing to change" + } + }, + { + "claim": "48 test call sites match `check` by name. None is resolved to this symbol, and none ran.", + "evidence": [ + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:271" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:275" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:234" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:238" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:249" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:481" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:522" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:663" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:677" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:678" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:680" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-math-binding-probe/tests/binding.rs:234" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:58" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:60" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:72" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:82" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:86" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:89" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:90" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:93" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:776" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:777" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:808" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:814" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:824" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:827" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:828" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:227" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:277" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:314" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:369" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:373" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:403" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:407" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:424" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/thought-git-probe/tests/receipt.rs:715" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/thought-git-probe/tests/receipt.rs:715" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:320" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:341" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:418" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:623" + } + ], + "falsifier": { + "description": "Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out.", + "status": "NOT_RUN" + }, + "id": "F2", + "kind": "coverage", + "location": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "lines": [ + 307, + 349 + ], + "symbol": "lance_graph_dt64::lane::{LaneBinding}::check" + }, + "provenance": { + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "rustc": "nightly-2026-09-17 (charon)", + "target": "x86_64-unknown-linux-gnu", + "tool": "lance-graph recon.py packet over a Charon/ruff_charon store" + }, + "reachability": { + "unknown": [ + { + "what": "test callers", + "why": "library targets only; macro regions and cfg(test) modules not indexed" + } + ] + }, + "suggestion": { + "refusal": "coverage gap; the next step is extraction, not a code change" + } + } + ], + "review": { + "base_sha": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "head_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "pr": 2000, + "repo": "AdaWorldAPI/lance-graph", + "reviewer": { + "input": "ReviewEvidencePacket", + "tool": "crai_report.py" + }, + "scope": "the same scope review, from a packet built at an older head" + }, + "run": { + "reasons": [ + "library targets only", + "consumer crates not in store", + "no test executed" + ], + "status": "PARTIAL" + } +} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input.json b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input.json new file mode 100644 index 000000000..7454f6486 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-input.json @@ -0,0 +1,566 @@ +{ + "findings": [ + { + "claim": "Changing `lance_graph_dt64::lane::{LaneBinding}::check` affects its in-store callers. Callers outside the analysed crates are not observed.", + "coverage": { + "observed_crate": "lance_graph_dt64 (lib target)", + "unsupported_regions": 10 + }, + "evidence": [ + { + "fresh": true, + "grade": "COMPILER_DERIVED", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "`lance_graph_dt64::lane::{LaneBinding}::borrow_all` calls `lance_graph_dt64::lane::{LaneBinding}::check`", + "tool": "charon", + "witness": "crates/lance-graph-dt64/src/lane.rs:386" + }, + { + "fresh": true, + "grade": "COMPILER_DERIVED", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "`lance_graph_dt64::lane::{LaneBinding}::lane` calls `lance_graph_dt64::lane::{LaneBinding}::check`", + "tool": "charon", + "witness": "crates/lance-graph-dt64/src/lane.rs:360" + } + ], + "falsifier": { + "description": "Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`.", + "status": "NOT_RUN" + }, + "id": "F1", + "kind": "impact", + "location": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "lines": [ + 307, + 349 + ], + "symbol": "lance_graph_dt64::lane::{LaneBinding}::check" + }, + "provenance": { + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "rustc": "nightly-2026-09-17 (charon)", + "target": "x86_64-unknown-linux-gnu", + "tool": "lance-graph recon.py packet over a Charon/ruff_charon store" + }, + "reachability": { + "confirmed": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "possible": [], + "unknown": [ + { + "observed_crates": [ + "lance_graph_contract", + "lance_graph_dt64", + "lance_graph_quack", + "lance_graph_r2il" + ], + "what": "callers in crates outside the store", + "why": "symbol is PUBLIC; only these crates' library targets were compiled" + }, + { + "what": "test targets, examples, cfg(test) modules", + "why": "store holds library targets only" + } + ] + }, + "suggestion": { + "refusal": "impact is a fact, not a defect; nothing to change" + } + }, + { + "claim": "48 test call sites match `check` by name. None is resolved to this symbol, and none ran.", + "evidence": [ + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:271" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:275" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:234" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:238" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:249" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:481" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:522" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:663" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:677" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:678" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dir-rmap-probe/tests/equivalence.rs:680" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/dr-math-binding-probe/tests/binding.rs:234" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:58" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:60" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:72" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:82" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:86" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:89" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:90" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:93" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:776" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:777" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:808" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:814" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:824" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:827" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/dr.rs:828" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:227" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:277" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:314" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:369" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:373" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:403" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:407" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", + "tool": "source search", + "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:424" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/thought-git-probe/tests/receipt.rs:715" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/thought-git-probe/tests/receipt.rs:715" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:320" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:341" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:418" + }, + { + "fresh": true, + "grade": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", + "tool": "source search", + "witness": "crates/lance-graph-report/src/lane_source/tests.rs:623" + } + ], + "falsifier": { + "description": "Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out.", + "status": "NOT_RUN" + }, + "id": "F2", + "kind": "coverage", + "location": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "lines": [ + 307, + 349 + ], + "symbol": "lance_graph_dt64::lane::{LaneBinding}::check" + }, + "provenance": { + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "rustc": "nightly-2026-09-17 (charon)", + "target": "x86_64-unknown-linux-gnu", + "tool": "lance-graph recon.py packet over a Charon/ruff_charon store" + }, + "reachability": { + "unknown": [ + { + "what": "test callers", + "why": "library targets only; macro regions and cfg(test) modules not indexed" + } + ] + }, + "suggestion": { + "refusal": "coverage gap; the next step is extraction, not a code change" + } + } + ], + "review": { + "base_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "head_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", + "pr": 2000, + "repo": "AdaWorldAPI/lance-graph", + "reviewer": { + "input": "ReviewEvidencePacket", + "tool": "crai_report.py" + }, + "scope": "scope review of a hypothetical change to LaneBinding::check (lance-graph-dt64 lib); base equals head, no diff" + }, + "run": { + "reasons": [ + "library targets only", + "consumer crates not in store", + "no test executed" + ], + "status": "PARTIAL" + } +} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.md b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.md new file mode 100644 index 000000000..7556b76f9 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.md @@ -0,0 +1,123 @@ +# Review: AdaWorldAPI/lance-graph #2000 + +- **Head:** `90fc5a76c12714d3bd00bcc5be96125aef74d89f` (base `0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb`) +- **Scope:** the same scope review, from a packet built at an older head +- **Reviewer:** {"input": "ReviewEvidencePacket", "tool": "crai_report.py"} +- **Run:** `PARTIAL` (library targets only; consumer crates not in store; no test executed) +- **Verdict:** INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (0 open finding(s), 2 stale, not revalidated at the head) +- **Semantics:** crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35). Severity, evidence and confidence are separate axes. + +## F1 · impact · stale + +**Location:** `crates/lance-graph-dt64/src/lane.rs:307-349` · `lance_graph_dt64::lane::{LaneBinding}::check` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | ASSERTED | UNKNOWN | + +**Claim** + +> Changing 'lance_graph_dt64::lane::{LaneBinding}::check' affects its in-store callers. Callers outside the analysed crates are not observed. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| COMPILER_DERIVED | | 'lance_graph_dt64::lane::{LaneBinding}::borrow_all' calls 'lance_graph_dt64::lane::{LaneBinding}::check' | `lance-graph:crates/lance-graph-dt64/src/lane.rs:386` | `0c9ae23ac6a2` (not current) | +| COMPILER_DERIVED | | 'lance_graph_dt64::lane::{LaneBinding}::lane' calls 'lance_graph_dt64::lane::{LaneBinding}::check' | `lance-graph:crates/lance-graph-dt64/src/lane.rs:360` | `0c9ae23ac6a2` (not current) | + +**Reachability** + +- confirmed: `lance_graph_dt64::lane::{LaneBinding}::borrow_all`, `lance_graph_dt64::lane::{LaneBinding}::lane` +- possible: none observed +- unknown: callers in crates outside the store (symbol is PUBLIC; only these crates' library targets were compiled); test targets, examples, cfg(test) modules (store holds library targets only) + +**Coverage** + +- observed_crate: lance_graph_dt64 (lib target) +- unsupported_regions: 10 + +**Falsifier** + +- Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`. +- status: `NOT_RUN` + +**No suggestion:** impact is a fact, not a defect; nothing to change + +## F2 · coverage · stale + +**Location:** `crates/lance-graph-dt64/src/lane.rs:307-349` · `lance_graph_dt64::lane::{LaneBinding}::check` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | ASSERTED | UNKNOWN | + +**Claim** + +> 48 test call sites match 'check' by name. None is resolved to this symbol, and none ran. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/cognitive-polyfill-probe/tests/p0.rs:271` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/cognitive-polyfill-probe/tests/p0.rs:275` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:234` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:238` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:249` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:481` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:522` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:663` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:677` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:678` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:680` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-isa-stream-lab/tests/integration/g2l.rs:136` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-isa-stream-lab/tests/integration/g2l.rs:263` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-math-binding-probe/tests/binding.rs:234` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:58` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:60` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:72` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:82` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:86` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:89` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:90` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:93` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:776` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:777` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:808` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:814` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:824` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:827` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:828` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:227` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:277` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:314` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:369` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:373` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:403` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:407` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:424` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/thought-git-probe/tests/receipt.rs:715` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/thought-git-probe/tests/receipt.rs:715` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/xlog-relcomp-probe/tests/dto_binding.rs:233` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:320` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:341` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:418` | `0c9ae23ac6a2` (not current) | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:623` | `0c9ae23ac6a2` (not current) | + +**Reachability** + +- confirmed: none observed +- possible: none observed +- unknown: test callers (library targets only; macro regions and cfg(test) modules not indexed) + +**Falsifier** + +- Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out. +- status: `NOT_RUN` + +**No suggestion:** coverage gap; the next step is extraction, not a code change diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.ndjson b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.ndjson new file mode 100644 index 000000000..4f09f6817 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review-stale.ndjson @@ -0,0 +1,4 @@ +{"base_sha": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "head_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "heads": {"AdaWorldAPI/lance-graph": "90fc5a76c12714d3bd00bcc5be96125aef74d89f"}, "pr": 2000, "repo": "AdaWorldAPI/lance-graph", "reviewer": {"input": "ReviewEvidencePacket", "tool": "crai_report.py"}, "scope": "the same scope review, from a packet built at an older head", "semantics": "crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35)", "type": "review_start"} +{"codegenInstructions": null, "comment": "Changing `lance_graph_dt64::lane::{LaneBinding}::check` affects its in-store callers. Callers outside the analysed crates are not observed.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {"observed_crate": "lance_graph_dt64 (lib target)", "unsupported_regions": 10}, "duplicate_of": null, "evidence": [{"fresh": true, "grade": "COMPILER_DERIVED", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "`lance_graph_dt64::lane::{LaneBinding}::borrow_all` calls `lance_graph_dt64::lane::{LaneBinding}::check`", "tool": "charon", "witness": "crates/lance-graph-dt64/src/lane.rs:386"}, {"fresh": true, "grade": "COMPILER_DERIVED", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "`lance_graph_dt64::lane::{LaneBinding}::lane` calls `lance_graph_dt64::lane::{LaneBinding}::check`", "tool": "charon", "witness": "crates/lance-graph-dt64/src/lane.rs:360"}], "evidence_class": "ASSERTED", "falsifier": {"command": null, "description": "Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "crates/lance-graph-dt64/src/lane.rs", "id": "F1", "kind": "impact", "lines": [307, 349], "provenance": {"crate_instance": "lance_graph_dt64 (lib target)", "features": "default", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "rustc": "nightly-2026-09-17 (charon)", "target": "x86_64-unknown-linux-gnu", "tool": "lance-graph recon.py packet over a Charon/ruff_charon store"}, "reachability": {"confirmed": ["lance_graph_dt64::lane::{LaneBinding}::borrow_all", "lance_graph_dt64::lane::{LaneBinding}::lane"], "possible": [], "unknown": [{"observed_crates": ["lance_graph_contract", "lance_graph_dt64", "lance_graph_quack", "lance_graph_r2il"], "what": "callers in crates outside the store", "why": "symbol is PUBLIC; only these crates' library targets were compiled"}, {"what": "test targets, examples, cfg(test) modules", "why": "store holds library targets only"}]}, "refusal": "impact is a fact, not a defect; nothing to change", "severity": null, "status": "stale", "status_detail": null, "suggestions": [], "symbol": "lance_graph_dt64::lane::{LaneBinding}::check", "type": "finding"} +{"codegenInstructions": null, "comment": "48 test call sites match `check` by name. None is resolved to this symbol, and none ran.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {}, "duplicate_of": null, "evidence": [{"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:271"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:275"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:234"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:238"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:249"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:481"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:522"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:663"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:677"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:678"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:680"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-math-binding-probe/tests/binding.rs:234"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:58"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:60"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:72"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:82"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:86"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:89"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:90"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:93"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:776"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:777"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:808"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:814"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:824"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:827"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:828"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:227"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:277"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:314"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:369"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:373"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:403"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:407"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:424"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/thought-git-probe/tests/receipt.rs:715"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/thought-git-probe/tests/receipt.rs:715"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:320"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:341"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:418"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:623"}], "evidence_class": "ASSERTED", "falsifier": {"command": null, "description": "Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "crates/lance-graph-dt64/src/lane.rs", "id": "F2", "kind": "coverage", "lines": [307, 349], "provenance": {"crate_instance": "lance_graph_dt64 (lib target)", "features": "default", "revision": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", "rustc": "nightly-2026-09-17 (charon)", "target": "x86_64-unknown-linux-gnu", "tool": "lance-graph recon.py packet over a Charon/ruff_charon store"}, "reachability": {"confirmed": [], "possible": [], "unknown": [{"what": "test callers", "why": "library targets only; macro regions and cfg(test) modules not indexed"}]}, "refusal": "coverage gap; the next step is extraction, not a code change", "severity": null, "status": "stale", "status_detail": null, "suggestions": [], "symbol": "lance_graph_dt64::lane::{LaneBinding}::check", "type": "finding"} +{"clean": false, "findings": 2, "reasons": ["library targets only", "consumer crates not in store", "no test executed"], "status": "PARTIAL", "type": "complete", "verdict": "INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (0 open finding(s), 2 stale, not revalidated at the head)"} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.md b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.md new file mode 100644 index 000000000..e9fcb4ea9 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.md @@ -0,0 +1,123 @@ +# Review: AdaWorldAPI/lance-graph #2000 + +- **Head:** `90fc5a76c12714d3bd00bcc5be96125aef74d89f` (base `90fc5a76c12714d3bd00bcc5be96125aef74d89f`) +- **Scope:** scope review of a hypothetical change to LaneBinding::check (lance-graph-dt64 lib); base equals head, no diff +- **Reviewer:** {"input": "ReviewEvidencePacket", "tool": "crai_report.py"} +- **Run:** `PARTIAL` (library targets only; consumer crates not in store; no test executed) +- **Verdict:** INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (2 open finding(s)) +- **Semantics:** crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35). Severity, evidence and confidence are separate axes. + +## F1 · impact · open + +**Location:** `crates/lance-graph-dt64/src/lane.rs:307-349` · `lance_graph_dt64::lane::{LaneBinding}::check` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | COMPILER_DERIVED | UNKNOWN | + +**Claim** + +> Changing 'lance_graph_dt64::lane::{LaneBinding}::check' affects its in-store callers. Callers outside the analysed crates are not observed. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| COMPILER_DERIVED | | 'lance_graph_dt64::lane::{LaneBinding}::borrow_all' calls 'lance_graph_dt64::lane::{LaneBinding}::check' | `lance-graph:crates/lance-graph-dt64/src/lane.rs:386` | `90fc5a76c127` | +| COMPILER_DERIVED | | 'lance_graph_dt64::lane::{LaneBinding}::lane' calls 'lance_graph_dt64::lane::{LaneBinding}::check' | `lance-graph:crates/lance-graph-dt64/src/lane.rs:360` | `90fc5a76c127` | + +**Reachability** + +- confirmed: `lance_graph_dt64::lane::{LaneBinding}::borrow_all`, `lance_graph_dt64::lane::{LaneBinding}::lane` +- possible: none observed +- unknown: callers in crates outside the store (symbol is PUBLIC; only these crates' library targets were compiled); test targets, examples, cfg(test) modules (store holds library targets only) + +**Coverage** + +- observed_crate: lance_graph_dt64 (lib target) +- unsupported_regions: 10 + +**Falsifier** + +- Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`. +- status: `NOT_RUN` + +**No suggestion:** impact is a fact, not a defect; nothing to change + +## F2 · coverage · open + +**Location:** `crates/lance-graph-dt64/src/lane.rs:307-349` · `lance_graph_dt64::lane::{LaneBinding}::check` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | SOURCE_STRUCTURE | UNKNOWN | + +**Claim** + +> 48 test call sites match 'check' by name. None is resolved to this symbol, and none ran. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/cognitive-polyfill-probe/tests/p0.rs:271` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/cognitive-polyfill-probe/tests/p0.rs:275` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:234` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:238` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:249` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:481` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:522` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:663` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:677` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:678` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dir-rmap-probe/tests/equivalence.rs:680` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-isa-stream-lab/tests/integration/g2l.rs:136` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-isa-stream-lab/tests/integration/g2l.rs:263` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/dr-math-binding-probe/tests/binding.rs:234` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:58` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:60` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:72` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:82` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:86` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:89` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:90` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:93` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:776` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:777` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:808` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:814` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:824` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:827` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/dr.rs:828` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:227` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:277` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:314` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:369` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:373` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:403` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:407` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: inside a macro region) | `lance-graph:crates/lance-graph-dt64/tests/lane_socket.rs:424` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/thought-git-probe/tests/receipt.rs:715` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/thought-git-probe/tests/receipt.rs:715` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/xlog-relcomp-probe/tests/dto_binding.rs:233` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:320` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:341` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:418` | `90fc5a76c127` | +| SOURCE_STRUCTURE | POSSIBLE | name match, receiver unresolved (UNKNOWN: file not indexed) | `lance-graph:crates/lance-graph-report/src/lane_source/tests.rs:623` | `90fc5a76c127` | + +**Reachability** + +- confirmed: none observed +- possible: none observed +- unknown: test callers (library targets only; macro regions and cfg(test) modules not indexed) + +**Falsifier** + +- Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out. +- status: `NOT_RUN` + +**No suggestion:** coverage gap; the next step is extraction, not a code change diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.ndjson b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.ndjson new file mode 100644 index 000000000..960c8ce63 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/lance-graph-2000/review.ndjson @@ -0,0 +1,4 @@ +{"base_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "head_sha": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "heads": {"AdaWorldAPI/lance-graph": "90fc5a76c12714d3bd00bcc5be96125aef74d89f"}, "pr": 2000, "repo": "AdaWorldAPI/lance-graph", "reviewer": {"input": "ReviewEvidencePacket", "tool": "crai_report.py"}, "scope": "scope review of a hypothetical change to LaneBinding::check (lance-graph-dt64 lib); base equals head, no diff", "semantics": "crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35)", "type": "review_start"} +{"codegenInstructions": null, "comment": "Changing `lance_graph_dt64::lane::{LaneBinding}::check` affects its in-store callers. Callers outside the analysed crates are not observed.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {"observed_crate": "lance_graph_dt64 (lib target)", "unsupported_regions": 10}, "duplicate_of": null, "evidence": [{"fresh": true, "grade": "COMPILER_DERIVED", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "`lance_graph_dt64::lane::{LaneBinding}::borrow_all` calls `lance_graph_dt64::lane::{LaneBinding}::check`", "tool": "charon", "witness": "crates/lance-graph-dt64/src/lane.rs:386"}, {"fresh": true, "grade": "COMPILER_DERIVED", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "`lance_graph_dt64::lane::{LaneBinding}::lane` calls `lance_graph_dt64::lane::{LaneBinding}::check`", "tool": "charon", "witness": "crates/lance-graph-dt64/src/lane.rs:360"}], "evidence_class": "COMPILER_DERIVED", "falsifier": {"command": null, "description": "Remove one witnessed call site and rebuild the store: the caller must leave `confirmed`.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "crates/lance-graph-dt64/src/lane.rs", "id": "F1", "kind": "impact", "lines": [307, 349], "provenance": {"crate_instance": "lance_graph_dt64 (lib target)", "features": "default", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "rustc": "nightly-2026-09-17 (charon)", "target": "x86_64-unknown-linux-gnu", "tool": "lance-graph recon.py packet over a Charon/ruff_charon store"}, "reachability": {"confirmed": ["lance_graph_dt64::lane::{LaneBinding}::borrow_all", "lance_graph_dt64::lane::{LaneBinding}::lane"], "possible": [], "unknown": [{"observed_crates": ["lance_graph_contract", "lance_graph_dt64", "lance_graph_quack", "lance_graph_r2il"], "what": "callers in crates outside the store", "why": "symbol is PUBLIC; only these crates' library targets were compiled"}, {"what": "test targets, examples, cfg(test) modules", "why": "store holds library targets only"}]}, "refusal": "impact is a fact, not a defect; nothing to change", "severity": null, "status": "open", "status_detail": null, "suggestions": [], "symbol": "lance_graph_dt64::lane::{LaneBinding}::check", "type": "finding"} +{"codegenInstructions": null, "comment": "48 test call sites match `check` by name. None is resolved to this symbol, and none ran.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {}, "duplicate_of": null, "evidence": [{"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:271"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/cognitive-polyfill-probe/tests/p0.rs:275"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:234"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:238"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:249"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:481"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:522"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:663"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:677"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:678"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dir-rmap-probe/tests/equivalence.rs:680"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/dr-math-binding-probe/tests/binding.rs:234"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:58"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:60"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:72"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:82"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:86"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:89"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:90"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:93"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:776"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:777"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:808"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:814"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:824"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:827"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/dr.rs:828"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:227"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:277"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:314"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:369"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:373"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:403"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:407"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: inside a macro region)", "tool": "source search", "witness": "crates/lance-graph-dt64/tests/lane_socket.rs:424"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/thought-git-probe/tests/receipt.rs:715"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/thought-git-probe/tests/receipt.rs:715"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:320"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:341"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:418"}, {"fresh": true, "grade": "SOURCE_STRUCTURE", "qualifier": "POSSIBLE", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "statement": "name match, receiver unresolved (UNKNOWN: file not indexed)", "tool": "source search", "witness": "crates/lance-graph-report/src/lane_source/tests.rs:623"}], "evidence_class": "SOURCE_STRUCTURE", "falsifier": {"command": null, "description": "Extract the test targets and re-run impact: name-matched sites either become confirmed callers or drop out.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "crates/lance-graph-dt64/src/lane.rs", "id": "F2", "kind": "coverage", "lines": [307, 349], "provenance": {"crate_instance": "lance_graph_dt64 (lib target)", "features": "default", "revision": "90fc5a76c12714d3bd00bcc5be96125aef74d89f", "rustc": "nightly-2026-09-17 (charon)", "target": "x86_64-unknown-linux-gnu", "tool": "lance-graph recon.py packet over a Charon/ruff_charon store"}, "reachability": {"confirmed": [], "possible": [], "unknown": [{"what": "test callers", "why": "library targets only; macro regions and cfg(test) modules not indexed"}]}, "refusal": "coverage gap; the next step is extraction, not a code change", "severity": null, "status": "open", "status_detail": null, "suggestions": [], "symbol": "lance_graph_dt64::lane::{LaneBinding}::check", "type": "finding"} +{"clean": false, "findings": 2, "reasons": ["library targets only", "consumer crates not in store", "no test executed"], "status": "PARTIAL", "type": "complete", "verdict": "INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (2 open finding(s))"} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review-input.json b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review-input.json new file mode 100644 index 000000000..ac50419e0 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review-input.json @@ -0,0 +1,301 @@ +{ + "findings": [ + { + "claim": "In the consumer's shipped code, the per-row power-of-two quotient specialisation in #422 has no call site to reach. #422's Rust changes are confined to the `u64x64` example target, which no crate can depend on. The G1 and G6 paths in the Charon store reach no per-row stock quotient: their mask predicates compare a stock lane against a scalar threshold (G6: `d * q`, built at method registration), and mask-risc evaluates `GeI32` as a plain compare. Not covered: mask-risc predicates other than `GeI32`, and G1's unshipped per-store min/argmin step (EXO-227 P1b).", + "coverage": { + "consumer_residuals": 0, + "consumer_store": "foundry_parity_lab library target", + "ndarray_library_rust_changed": 0, + "ndarray_other_rust_outside_examples": 0 + }, + "evidence": [ + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/ndarray", + "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", + "statement": "#422 changes 6 files: 3 Rust, 3 in example target(s) ['u64x64']; library Rust files changed: 0; other Rust files changed: 0", + "tool": "git", + "witness": "97654984da60..f279fe75f4a5" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "G1 capacity (arm_b): `arm_b::{ItemQuery<'s>}::max_quantities` reaches 35 functions (3 only via dispatch or a closure body); divisions reached: [\"arm_b::{ItemQuery<'s>}::cook_ops\"]; other crates reached: ['lance_graph_mask_risc']", + "tool": "charon c69e231 + ruff_charon", + "witness": "store" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "G1 capacity (arm_b): `arm_b::{ItemQuery<'s>}::network_capacity` reaches 27 functions (2 only via dispatch or a closure body); divisions reached: [\"arm_b::{ItemQuery<'s>}::cook_ops\"]; other crates reached: ['lance_graph_mask_risc']", + "tool": "charon c69e231 + ruff_charon", + "witness": "store" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "G6 CanCook (method): `method::execute_can_cook` reaches 57 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg']", + "tool": "charon c69e231 + ruff_charon", + "witness": "store" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "G6 CanCook (method): `method::{PreparedMethod<'s>}::prepare` reaches 49 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg']", + "tool": "charon c69e231 + ruff_charon", + "witness": "store" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "G6 CanCook (method): `method::{PreparedMethod<'s>}::run` reaches 12 functions (0 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_mask_risc']", + "tool": "charon c69e231 + ruff_charon", + "witness": "store" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "integer division on an executable path, in `arm_b::{ItemQuery<'s>}::cook_ops`: `(q + r.per_staff - 1) / r.per_staff`", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/arm_b.rs:245" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "mask-predicate threshold in `arm_b::{ItemQuery<'s>}::cook_ops`: `t: d * q` (names no lane)", + "tool": "source span read", + "witness": "crates/foundry-parity-lab/src/arm_b.rs:235" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "mask-predicate threshold in `arm_b::{ItemQuery<'s>}::cook_ops`: `t: (q + r.per_staff - 1) / r.per_staff` (names no lane)", + "tool": "source span read", + "witness": "crates/foundry-parity-lab/src/arm_b.rs:245" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "integer division at G6 method registration (`method::can_cook_guard`, reached from ['method::methods'], not from the G6 execution entry points): `(q + r.per_staff - 1) / r.per_staff`", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/method.rs:58" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "mask-predicate threshold in `method::can_cook_guard`: `d * q` (names no lane)", + "tool": "source span read", + "witness": "crates/foundry-parity-lab/src/method.rs:55" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "mask-predicate threshold in `method::can_cook_guard`: `staff` (names no lane)", + "tool": "source span read", + "witness": "crates/foundry-parity-lab/src/method.rs:59" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": "POSSIBLE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "reference arm `oracle::{Oracle}::network_capacity` reaches `oracle::{Oracle}::can_cook` through a closure body linked to its parent by Charon's name only: `fx::stock(s, k, self.restocked) / d`", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/oracle.rs:62" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": "POSSIBLE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "reference arm `arm_a::{Objects}::network_capacity` reaches `arm_a::{Objects}::can_cook` through a closure body linked to its parent by Charon's name only: `v / d`", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/arm_a.rs:75" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "mask-risc `Pred::GeI32 { lane: u16, t: i32 }` is `lane[i] >= t` with `t` a scalar `i32`", + "tool": "source span read", + "witness": "crates/lance-graph-mask-risc/src/ir.rs:133" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "the executor passes `t` unchanged to `ndarray::simd::ge_i32_to_mask`", + "tool": "source span read", + "witness": "crates/lance-graph-mask-risc/src/exec.rs:588" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/ndarray", + "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", + "statement": "`ge_i32_to_mask` is the complement of `lt_i32_to_mask`, a compare of each lane value against the splatted threshold; no division", + "tool": "git", + "witness": "src/simd_masking_ops.rs:3449" + } + ], + "falsifier": { + "description": "Run Charon on lance-graph-mask-risc and re-run the reach from the G1/G6 entry points into it: any `DivisionByZero` assertion on a path that takes a stock lane as an operand refutes the claim. Smallest next verification: the predicate kinds other than `GeI32` that the consumer builds.", + "status": "NOT_RUN" + }, + "id": "Q1", + "kind": "admission", + "location": { + "file": "examples/u64x64/route_c.rs", + "symbol": "u64x64 route C / A power-of-two quotient arms" + }, + "reachability": { + "confirmed": [ + "G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::max_quantities", + "G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::network_capacity", + "G6 CanCook (method): method::execute_can_cook", + "G6 CanCook (method): method::{PreparedMethod<'s>}::prepare", + "G6 CanCook (method): method::{PreparedMethod<'s>}::run" + ], + "possible": [ + "oracle::{Oracle}::network_capacity \u2192 oracle::{Oracle}::can_cook", + "arm_a::{Objects}::network_capacity \u2192 arm_a::{Objects}::can_cook" + ], + "unknown": [ + { + "what": "lance-graph-mask-risc beyond `GeI32`", + "why": "not in the Charon store; only the `GeI32` evaluation path was read, as source" + }, + { + "what": "G1 per-store min/argmin (limiting resource)", + "why": "not shipped: open in EXO-227 (P1b); this review covers shipped code only" + }, + { + "what": "production recipe divisor distribution", + "why": "no production source; the only population is the synthetic fixture" + }, + { + "what": "96 closure bodies in the crate", + "why": "linked to their parent by Charon's naming only (POSSIBLE), not by a call row" + } + ] + }, + "suggestion": { + "refusal": "an admission finding, not a defect: no code change follows from the evidence" + } + }, + { + "claim": "The executable path does evaluate integer division: `(q + per_staff - 1) / per_staff`. It is the threshold operand of a mask predicate and names no lane, so it is computed once when the predicate is built, not per row. G6 has the same ceiling division at method registration. In shipped code, a per-row quotient specialisation has nothing to apply to.", + "evidence": [ + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "division assertion (zero and overflow) at this site", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/arm_b.rs:245" + }, + { + "grade": "COMPILER_DERIVED", + "qualifier": null, + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "division assertion at G6 method registration", + "tool": "charon c69e231 + ruff_charon", + "witness": "crates/foundry-parity-lab/src/method.rs:58" + }, + { + "grade": "SOURCE_STRUCTURE", + "repo": "AdaWorldAPI/lance-graph", + "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", + "statement": "`t: (q + r.per_staff - 1) / r.per_staff` (names no lane)", + "tool": "source span read", + "witness": "crates/foundry-parity-lab/src/arm_b.rs:245" + } + ], + "falsifier": { + "description": "Read the Pred::GeI32 threshold sites: the division's operands are query parameters, not lanes.", + "status": "NOT_RUN" + }, + "id": "Q2", + "kind": "research", + "location": { + "file": "crates/foundry-parity-lab/src/arm_b.rs", + "symbol": "arm_b::{ItemQuery}::cook_ops" + }, + "reachability": { + "confirmed": [ + "arm_b::{ItemQuery<'s>}::cook_ops", + "method::can_cook_guard" + ] + }, + "suggestion": { + "base_sha": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", + "file": ".claude/plans/u64x64-llvm-probe-v1.md (ndarray)", + "line": 921, + "text": "Narrow 'No quotient is evaluated on the executable path' to 'no per-row stock quotient is evaluated': a scalar ceiling division builds the staff threshold." + } + }, + { + "claim": "The PR's benchmark results (power-of-two shift no-regret; best route only on AVX-512 hot) were not re-run here.", + "evidence": [ + { + "grade": "ASSERTED", + "repo": "AdaWorldAPI/ndarray", + "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", + "statement": "benchmark results and disable runs, author-reported (plan \u00a717)", + "witness": ".claude/plans/u64x64-llvm-probe-v1.md:949" + } + ], + "falsifier": { + "description": "Per tier T in v2/v3/v4, build with `env -u RUSTFLAGS cargo --config .cargo/config-$T.toml rustc --release --example u64x64` (plan \u00a74) and run `u64x64 bench ` for the \u00a716 scenarios; compare with \u00a717. The F7/F8 disable arms (test `d > 0` instead of `is_power_of_two`) are parity checks: they must turn `u64x64 check` red, and say nothing about speed.", + "status": "NOT_RUN" + }, + "id": "Q3", + "kind": "research", + "location": { + "file": "examples/u64x64/main.rs" + }, + "suggestion": { + "refusal": "a performance claim outside this review's evidence; no defect follows" + } + } + ], + "review": { + "base_sha": "97654984da601c4705bc2350aa586d39d7c57fd2", + "head_sha": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", + "heads": { + "AdaWorldAPI/lance-graph": "c7151a1ecb03d4523ece505e42de1e77ed23111e" + }, + "pr": 422, + "repo": "AdaWorldAPI/ndarray", + "reviewer": { + "tool": "crai_report.py + build_q8_evidence.py" + }, + "scope": "PR #422 commits; consumer: lance-graph foundry-parity-lab (lib), extracted at c7151a1ecb03d4523ece505e42de1e77ed23111e; foundry-parity-lab and mask-risc unchanged at 317c4ffc04cb557d1142fd1331a0ccaedcd286de" + }, + "run": { + "reasons": [ + "mask-risc not in the Charon store (GeI32 read as source only)", + "no test or benchmark executed" + ], + "status": "PARTIAL" + } +} diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.md b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.md new file mode 100644 index 000000000..d34702b34 --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.md @@ -0,0 +1,123 @@ +# Review: AdaWorldAPI/ndarray #422 + +- **Head:** `f279fe75f4a5c4aff75f473a38508cf9e9d20aea` (base `97654984da601c4705bc2350aa586d39d7c57fd2`) +- **Evidence head, AdaWorldAPI/lance-graph:** `c7151a1ecb03d4523ece505e42de1e77ed23111e` +- **Scope:** PR #422 commits; consumer: lance-graph foundry-parity-lab (lib), extracted at c7151a1ecb03d4523ece505e42de1e77ed23111e; foundry-parity-lab and mask-risc unchanged at 317c4ffc04cb557d1142fd1331a0ccaedcd286de +- **Reviewer:** {"tool": "crai_report.py + build_q8_evidence.py"} +- **Run:** `PARTIAL` (mask-risc not in the Charon store (GeI32 read as source only); no test or benchmark executed) +- **Verdict:** INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (3 open finding(s)) +- **Semantics:** crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35). Severity, evidence and confidence are separate axes. + +## Q1 · admission · open + +**Location:** `examples/u64x64/route_c.rs` · `u64x64 route C / A power-of-two quotient arms` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | COMPILER_DERIVED | UNKNOWN | + +**Claim** + +> In the consumer's shipped code, the per-row power-of-two quotient specialisation in #422 has no call site to reach. #422's Rust changes are confined to the 'u64x64' example target, which no crate can depend on. The G1 and G6 paths in the Charon store reach no per-row stock quotient: their mask predicates compare a stock lane against a scalar threshold (G6: 'd * q', built at method registration), and mask-risc evaluates 'GeI32' as a plain compare. Not covered: mask-risc predicates other than 'GeI32', and G1's unshipped per-store min/argmin step (EXO-227 P1b). + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| SOURCE_STRUCTURE | | #422 changes 6 files: 3 Rust, 3 in example target(s) ['u64x64']; library Rust files changed: 0; other Rust files changed: 0 | `ndarray:97654984da60..f279fe75f4a5` | `f279fe75f4a5` | +| COMPILER_DERIVED | | G1 capacity (arm_b): 'arm_b::{ItemQuery<'s>}::max_quantities' reaches 35 functions (3 only via dispatch or a closure body); divisions reached: ["arm_b::{ItemQuery<'s>}::cook_ops"]; other crates reached: ['lance_graph_mask_risc'] | `lance-graph:store` | `c7151a1ecb03` | +| COMPILER_DERIVED | | G1 capacity (arm_b): 'arm_b::{ItemQuery<'s>}::network_capacity' reaches 27 functions (2 only via dispatch or a closure body); divisions reached: ["arm_b::{ItemQuery<'s>}::cook_ops"]; other crates reached: ['lance_graph_mask_risc'] | `lance-graph:store` | `c7151a1ecb03` | +| COMPILER_DERIVED | | G6 CanCook (method): 'method::execute_can_cook' reaches 57 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg'] | `lance-graph:store` | `c7151a1ecb03` | +| COMPILER_DERIVED | | G6 CanCook (method): 'method::{PreparedMethod<'s>}::prepare' reaches 49 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg'] | `lance-graph:store` | `c7151a1ecb03` | +| COMPILER_DERIVED | | G6 CanCook (method): 'method::{PreparedMethod<'s>}::run' reaches 12 functions (0 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_mask_risc'] | `lance-graph:store` | `c7151a1ecb03` | +| COMPILER_DERIVED | | integer division on an executable path, in 'arm_b::{ItemQuery<'s>}::cook_ops': '(q + r.per_staff - 1) / r.per_staff' | `lance-graph:crates/foundry-parity-lab/src/arm_b.rs:245` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | mask-predicate threshold in 'arm_b::{ItemQuery<'s>}::cook_ops': 't: d * q' (names no lane) | `lance-graph:crates/foundry-parity-lab/src/arm_b.rs:235` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | mask-predicate threshold in 'arm_b::{ItemQuery<'s>}::cook_ops': 't: (q + r.per_staff - 1) / r.per_staff' (names no lane) | `lance-graph:crates/foundry-parity-lab/src/arm_b.rs:245` | `c7151a1ecb03` | +| COMPILER_DERIVED | | integer division at G6 method registration ('method::can_cook_guard', reached from ['method::methods'], not from the G6 execution entry points): '(q + r.per_staff - 1) / r.per_staff' | `lance-graph:crates/foundry-parity-lab/src/method.rs:58` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | mask-predicate threshold in 'method::can_cook_guard': 'd * q' (names no lane) | `lance-graph:crates/foundry-parity-lab/src/method.rs:55` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | mask-predicate threshold in 'method::can_cook_guard': 'staff' (names no lane) | `lance-graph:crates/foundry-parity-lab/src/method.rs:59` | `c7151a1ecb03` | +| COMPILER_DERIVED | POSSIBLE | reference arm 'oracle::{Oracle}::network_capacity' reaches 'oracle::{Oracle}::can_cook' through a closure body linked to its parent by Charon's name only: 'fx::stock(s, k, self.restocked) / d' | `lance-graph:crates/foundry-parity-lab/src/oracle.rs:62` | `c7151a1ecb03` | +| COMPILER_DERIVED | POSSIBLE | reference arm 'arm_a::{Objects}::network_capacity' reaches 'arm_a::{Objects}::can_cook' through a closure body linked to its parent by Charon's name only: 'v / d' | `lance-graph:crates/foundry-parity-lab/src/arm_a.rs:75` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | mask-risc 'Pred::GeI32 { lane: u16, t: i32 }' is 'lane[i] >= t' with 't' a scalar 'i32' | `lance-graph:crates/lance-graph-mask-risc/src/ir.rs:133` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | the executor passes 't' unchanged to 'ndarray::simd::ge_i32_to_mask' | `lance-graph:crates/lance-graph-mask-risc/src/exec.rs:588` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | 'ge_i32_to_mask' is the complement of 'lt_i32_to_mask', a compare of each lane value against the splatted threshold; no division | `ndarray:src/simd_masking_ops.rs:3449` | `f279fe75f4a5` | + +**Reachability** + +- confirmed: `G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::max_quantities`, `G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::network_capacity`, `G6 CanCook (method): method::execute_can_cook`, `G6 CanCook (method): method::{PreparedMethod<'s>}::prepare`, `G6 CanCook (method): method::{PreparedMethod<'s>}::run` +- possible: `arm_a::{Objects}::network_capacity → arm_a::{Objects}::can_cook`, `oracle::{Oracle}::network_capacity → oracle::{Oracle}::can_cook` +- unknown: 96 closure bodies in the crate (linked to their parent by Charon's naming only (POSSIBLE), not by a call row); G1 per-store min/argmin (limiting resource) (not shipped: open in EXO-227 (P1b); this review covers shipped code only); lance-graph-mask-risc beyond `GeI32` (not in the Charon store; only the `GeI32` evaluation path was read, as source); production recipe divisor distribution (no production source; the only population is the synthetic fixture) + +**Coverage** + +- consumer_residuals: 0 +- consumer_store: foundry_parity_lab library target +- ndarray_library_rust_changed: 0 +- ndarray_other_rust_outside_examples: 0 + +**Falsifier** + +- Run Charon on lance-graph-mask-risc and re-run the reach from the G1/G6 entry points into it: any `DivisionByZero` assertion on a path that takes a stock lane as an operand refutes the claim. Smallest next verification: the predicate kinds other than `GeI32` that the consumer builds. +- status: `NOT_RUN` + +**No suggestion:** an admission finding, not a defect: no code change follows from the evidence + +## Q2 · research · open + +**Location:** `crates/foundry-parity-lab/src/arm_b.rs` · `arm_b::{ItemQuery}::cook_ops` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | COMPILER_DERIVED | UNKNOWN | + +**Claim** + +> The executable path does evaluate integer division: '(q + per_staff - 1) / per_staff'. It is the threshold operand of a mask predicate and names no lane, so it is computed once when the predicate is built, not per row. G6 has the same ceiling division at method registration. In shipped code, a per-row quotient specialisation has nothing to apply to. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| COMPILER_DERIVED | | division assertion (zero and overflow) at this site | `lance-graph:crates/foundry-parity-lab/src/arm_b.rs:245` | `c7151a1ecb03` | +| COMPILER_DERIVED | | division assertion at G6 method registration | `lance-graph:crates/foundry-parity-lab/src/method.rs:58` | `c7151a1ecb03` | +| SOURCE_STRUCTURE | | 't: (q + r.per_staff - 1) / r.per_staff' (names no lane) | `lance-graph:crates/foundry-parity-lab/src/arm_b.rs:245` | `c7151a1ecb03` | + +**Reachability** + +- confirmed: `arm_b::{ItemQuery<'s>}::cook_ops`, `method::can_cook_guard` +- possible: none observed +- unknown: none recorded + +**Falsifier** + +- Read the Pred::GeI32 threshold sites: the division's operands are query parameters, not lanes. +- status: `NOT_RUN` + +**Suggestion** (inert proposal; applying it needs the owner's approval and revalidation against `f279fe75f4a5`) + +> Narrow 'No quotient is evaluated on the executable path' to 'no per-row stock quotient is evaluated': a scalar ceiling division builds the staff threshold. + +## Q3 · research · open + +**Location:** `examples/u64x64/main.rs` + +| severity | evidence class | confidence | +|---|---|---| +| not assessed | ASSERTED | UNKNOWN | + +**Claim** + +> The PR's benchmark results (power-of-two shift no-regret; best route only on AVX-512 hot) were not re-run here. + +**Evidence** + +| grade | qualifier | statement | witness | revision | +|---|---|---|---|---| +| ASSERTED | | benchmark results and disable runs, author-reported (plan §17) | `ndarray:.claude/plans/u64x64-llvm-probe-v1.md:949` | `f279fe75f4a5` | + +**Falsifier** + +- Per tier T in v2/v3/v4, build with `env -u RUSTFLAGS cargo --config .cargo/config-$T.toml rustc --release --example u64x64` (plan §4) and run `u64x64 bench ` for the §16 scenarios; compare with §17. The F7/F8 disable arms (test `d > 0` instead of `is_power_of_two`) are parity checks: they must turn `u64x64 check` red, and say nothing about speed. +- status: `NOT_RUN` + +**No suggestion:** a performance claim outside this review's evidence; no defect follows diff --git a/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.ndjson b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.ndjson new file mode 100644 index 000000000..eabe582df --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/examples/ndarray-422/review.ndjson @@ -0,0 +1,5 @@ +{"base_sha": "97654984da601c4705bc2350aa586d39d7c57fd2", "head_sha": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", "heads": {"AdaWorldAPI/lance-graph": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "AdaWorldAPI/ndarray": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea"}, "pr": 422, "repo": "AdaWorldAPI/ndarray", "reviewer": {"tool": "crai_report.py + build_q8_evidence.py"}, "scope": "PR #422 commits; consumer: lance-graph foundry-parity-lab (lib), extracted at c7151a1ecb03d4523ece505e42de1e77ed23111e; foundry-parity-lab and mask-risc unchanged at 317c4ffc04cb557d1142fd1331a0ccaedcd286de", "semantics": "crai-rs CR-H0-REVIEW-SEMANTICS (c43ab35)", "type": "review_start"} +{"codegenInstructions": null, "comment": "In the consumer's shipped code, the per-row power-of-two quotient specialisation in #422 has no call site to reach. #422's Rust changes are confined to the `u64x64` example target, which no crate can depend on. The G1 and G6 paths in the Charon store reach no per-row stock quotient: their mask predicates compare a stock lane against a scalar threshold (G6: `d * q`, built at method registration), and mask-risc evaluates `GeI32` as a plain compare. Not covered: mask-risc predicates other than `GeI32`, and G1's unshipped per-store min/argmin step (EXO-227 P1b).", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {"consumer_residuals": 0, "consumer_store": "foundry_parity_lab library target", "ndarray_library_rust_changed": 0, "ndarray_other_rust_outside_examples": 0}, "duplicate_of": null, "evidence": [{"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/ndarray", "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", "statement": "#422 changes 6 files: 3 Rust, 3 in example target(s) ['u64x64']; library Rust files changed: 0; other Rust files changed: 0", "tool": "git", "witness": "97654984da60..f279fe75f4a5"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "G1 capacity (arm_b): `arm_b::{ItemQuery<'s>}::max_quantities` reaches 35 functions (3 only via dispatch or a closure body); divisions reached: [\"arm_b::{ItemQuery<'s>}::cook_ops\"]; other crates reached: ['lance_graph_mask_risc']", "tool": "charon c69e231 + ruff_charon", "witness": "store"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "G1 capacity (arm_b): `arm_b::{ItemQuery<'s>}::network_capacity` reaches 27 functions (2 only via dispatch or a closure body); divisions reached: [\"arm_b::{ItemQuery<'s>}::cook_ops\"]; other crates reached: ['lance_graph_mask_risc']", "tool": "charon c69e231 + ruff_charon", "witness": "store"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "G6 CanCook (method): `method::execute_can_cook` reaches 57 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg']", "tool": "charon c69e231 + ruff_charon", "witness": "store"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "G6 CanCook (method): `method::{PreparedMethod<'s>}::prepare` reaches 49 functions (7 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_contract', 'lance_graph_dt64', 'lance_graph_mask_risc', 'lance_graph_relalg']", "tool": "charon c69e231 + ruff_charon", "witness": "store"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "G6 CanCook (method): `method::{PreparedMethod<'s>}::run` reaches 12 functions (0 only via dispatch or a closure body); divisions reached: none; other crates reached: ['lance_graph_mask_risc']", "tool": "charon c69e231 + ruff_charon", "witness": "store"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "integer division on an executable path, in `arm_b::{ItemQuery<'s>}::cook_ops`: `(q + r.per_staff - 1) / r.per_staff`", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/arm_b.rs:245"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "mask-predicate threshold in `arm_b::{ItemQuery<'s>}::cook_ops`: `t: d * q` (names no lane)", "tool": "source span read", "witness": "crates/foundry-parity-lab/src/arm_b.rs:235"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "mask-predicate threshold in `arm_b::{ItemQuery<'s>}::cook_ops`: `t: (q + r.per_staff - 1) / r.per_staff` (names no lane)", "tool": "source span read", "witness": "crates/foundry-parity-lab/src/arm_b.rs:245"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "integer division at G6 method registration (`method::can_cook_guard`, reached from ['method::methods'], not from the G6 execution entry points): `(q + r.per_staff - 1) / r.per_staff`", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/method.rs:58"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "mask-predicate threshold in `method::can_cook_guard`: `d * q` (names no lane)", "tool": "source span read", "witness": "crates/foundry-parity-lab/src/method.rs:55"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "mask-predicate threshold in `method::can_cook_guard`: `staff` (names no lane)", "tool": "source span read", "witness": "crates/foundry-parity-lab/src/method.rs:59"}, {"grade": "COMPILER_DERIVED", "qualifier": "POSSIBLE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "reference arm `oracle::{Oracle}::network_capacity` reaches `oracle::{Oracle}::can_cook` through a closure body linked to its parent by Charon's name only: `fx::stock(s, k, self.restocked) / d`", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/oracle.rs:62"}, {"grade": "COMPILER_DERIVED", "qualifier": "POSSIBLE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "reference arm `arm_a::{Objects}::network_capacity` reaches `arm_a::{Objects}::can_cook` through a closure body linked to its parent by Charon's name only: `v / d`", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/arm_a.rs:75"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "mask-risc `Pred::GeI32 { lane: u16, t: i32 }` is `lane[i] >= t` with `t` a scalar `i32`", "tool": "source span read", "witness": "crates/lance-graph-mask-risc/src/ir.rs:133"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "the executor passes `t` unchanged to `ndarray::simd::ge_i32_to_mask`", "tool": "source span read", "witness": "crates/lance-graph-mask-risc/src/exec.rs:588"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/ndarray", "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", "statement": "`ge_i32_to_mask` is the complement of `lt_i32_to_mask`, a compare of each lane value against the splatted threshold; no division", "tool": "git", "witness": "src/simd_masking_ops.rs:3449"}], "evidence_class": "COMPILER_DERIVED", "falsifier": {"command": null, "description": "Run Charon on lance-graph-mask-risc and re-run the reach from the G1/G6 entry points into it: any `DivisionByZero` assertion on a path that takes a stock lane as an operand refutes the claim. Smallest next verification: the predicate kinds other than `GeI32` that the consumer builds.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "examples/u64x64/route_c.rs", "id": "Q1", "kind": "admission", "lines": null, "provenance": null, "reachability": {"confirmed": ["G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::max_quantities", "G1 capacity (arm_b): arm_b::{ItemQuery<'s>}::network_capacity", "G6 CanCook (method): method::execute_can_cook", "G6 CanCook (method): method::{PreparedMethod<'s>}::prepare", "G6 CanCook (method): method::{PreparedMethod<'s>}::run"], "possible": ["arm_a::{Objects}::network_capacity → arm_a::{Objects}::can_cook", "oracle::{Oracle}::network_capacity → oracle::{Oracle}::can_cook"], "unknown": [{"what": "96 closure bodies in the crate", "why": "linked to their parent by Charon's naming only (POSSIBLE), not by a call row"}, {"what": "G1 per-store min/argmin (limiting resource)", "why": "not shipped: open in EXO-227 (P1b); this review covers shipped code only"}, {"what": "lance-graph-mask-risc beyond `GeI32`", "why": "not in the Charon store; only the `GeI32` evaluation path was read, as source"}, {"what": "production recipe divisor distribution", "why": "no production source; the only population is the synthetic fixture"}]}, "refusal": "an admission finding, not a defect: no code change follows from the evidence", "severity": null, "status": "open", "status_detail": null, "suggestions": [], "symbol": "u64x64 route C / A power-of-two quotient arms", "type": "finding"} +{"codegenInstructions": null, "comment": "The executable path does evaluate integer division: `(q + per_staff - 1) / per_staff`. It is the threshold operand of a mask predicate and names no lane, so it is computed once when the predicate is built, not per row. G6 has the same ceiling division at method registration. In shipped code, a per-row quotient specialisation has nothing to apply to.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {}, "duplicate_of": null, "evidence": [{"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "division assertion (zero and overflow) at this site", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/arm_b.rs:245"}, {"grade": "COMPILER_DERIVED", "qualifier": null, "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "division assertion at G6 method registration", "tool": "charon c69e231 + ruff_charon", "witness": "crates/foundry-parity-lab/src/method.rs:58"}, {"grade": "SOURCE_STRUCTURE", "repo": "AdaWorldAPI/lance-graph", "revision": "c7151a1ecb03d4523ece505e42de1e77ed23111e", "statement": "`t: (q + r.per_staff - 1) / r.per_staff` (names no lane)", "tool": "source span read", "witness": "crates/foundry-parity-lab/src/arm_b.rs:245"}], "evidence_class": "COMPILER_DERIVED", "falsifier": {"command": null, "description": "Read the Pred::GeI32 threshold sites: the division's operands are query parameters, not lanes.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "crates/foundry-parity-lab/src/arm_b.rs", "id": "Q2", "kind": "research", "lines": null, "provenance": null, "reachability": {"confirmed": ["arm_b::{ItemQuery<'s>}::cook_ops", "method::can_cook_guard"], "possible": [], "unknown": []}, "refusal": null, "severity": null, "status": "open", "status_detail": null, "suggestions": [{"base_sha": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", "file": ".claude/plans/u64x64-llvm-probe-v1.md (ndarray)", "line": 921, "text": "Narrow 'No quotient is evaluated on the executable path' to 'no per-row stock quotient is evaluated': a scalar ceiling division builds the staff threshold."}], "symbol": "arm_b::{ItemQuery}::cook_ops", "type": "finding"} +{"codegenInstructions": null, "comment": "The PR's benchmark results (power-of-two shift no-regret; best route only on AVX-512 hot) were not re-run here.", "confidence": {"source": null, "value": "UNKNOWN"}, "coverage": {}, "duplicate_of": null, "evidence": [{"grade": "ASSERTED", "repo": "AdaWorldAPI/ndarray", "revision": "f279fe75f4a5c4aff75f473a38508cf9e9d20aea", "statement": "benchmark results and disable runs, author-reported (plan §17)", "witness": ".claude/plans/u64x64-llvm-probe-v1.md:949"}], "evidence_class": "ASSERTED", "falsifier": {"command": null, "description": "Per tier T in v2/v3/v4, build with `env -u RUSTFLAGS cargo --config .cargo/config-$T.toml rustc --release --example u64x64` (plan §4) and run `u64x64 bench ` for the §16 scenarios; compare with §17. The F7/F8 disable arms (test `d > 0` instead of `is_power_of_two`) are parity checks: they must turn `u64x64 check` red, and say nothing about speed.", "disable_run": null, "status": "NOT_RUN"}, "fileName": "examples/u64x64/main.rs", "id": "Q3", "kind": "research", "lines": null, "provenance": null, "reachability": {"confirmed": [], "possible": [], "unknown": []}, "refusal": "a performance claim outside this review's evidence; no defect follows", "severity": null, "status": "open", "status_detail": null, "suggestions": [], "symbol": null, "type": "finding"} +{"clean": false, "findings": 3, "reasons": ["mask-risc not in the Charon store (GeI32 read as source only)", "no test or benchmark executed"], "status": "PARTIAL", "type": "complete", "verdict": "INCOMPLETE REVIEW: run PARTIAL; not a defect verdict (3 open finding(s))"} diff --git a/.claude/probes/2026-11-11-crai-report-1/packet-LaneBinding-check.json b/.claude/probes/2026-11-11-crai-report-1/packet-LaneBinding-check.json new file mode 100644 index 000000000..50b4a66fd --- /dev/null +++ b/.claude/probes/2026-11-11-crai-report-1/packet-LaneBinding-check.json @@ -0,0 +1,553 @@ +{ + "packet": "ReviewEvidencePacket (prototype, read-only; not a vendor API)", + "identity": { + "repo_id": "AdaWorldAPI/lance-graph", + "commit_sha": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "cargo_lock_sha": "NOT_TRACKED (repository tracks no Cargo.lock)", + "crate_instance": "lance_graph_dt64 (lib target)", + "features": "default", + "target_triple": "x86_64-unknown-linux-gnu", + "rustc_version": "nightly-2026-09-17 (charon)", + "file_blob_sha": "a0aa6e6658872d07c93990095d2fab2beff1487d", + "source_span": { + "file": "crates/lance-graph-dt64/src/lane.rs", + "bytes": [ + 10592, + 12497 + ], + "lines": [ + 307, + 349 + ] + } + }, + "changed_source": { + "subject": "lance_graph_dt64::lane::{LaneBinding}::check", + "base": "0c9ae23ac6a20ca6cd0c50da7e7a69e0f5df34eb", + "diff": "diff --git a/crates/lance-graph-dt64/src/lane.rs b/crates/lance-graph-dt64/src/lane.rs\nindex a0aa6e665..d21a5b602 100644\n--- a/crates/lance-graph-dt64/src/lane.rs\n+++ b/crates/lance-graph-dt64/src/lane.rs\n@@ -312,7 +312,7 @@ impl LaneBinding {\n source: &S,\n ) -> Result<(), LaneRefusal> {\n source_owns(frames, source)?;\n- frames.check(&self.frame).map_err(LaneRefusal::Frame)?;\n+ frames.check(&self.frame).map_err(LaneRefusal::Frame)?; // probe edit\n current_route(store, self.route.position, self.class)?;\n // Revision and count only: the ordered predicate witness was checked\n // when the binding was made (bind_lanes resolves through the\n", + "stale": false, + "freshness": "FRESH", + "dependency_evidence_stale": false + }, + "affected_symbols": { + "confirmed": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "possible": [], + "stale": [], + "unknown": [ + { + "what": "callers in crates outside the store", + "observed_crates": [ + "lance_graph_contract", + "lance_graph_dt64", + "lance_graph_quack", + "lance_graph_r2il" + ], + "why": "symbol is PUBLIC; only these crates' library targets were compiled" + }, + { + "what": "test targets, examples, cfg(test) modules", + "why": "store holds library targets only" + } + ] + }, + "witnessed_edges": [ + { + "caller": "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "callee": "lance_graph_dt64::lane::{LaneBinding}::check", + "sites": [ + "crates/lance-graph-dt64/src/lane.rs:386" + ], + "fresh": true + }, + { + "caller": "lance_graph_dt64::lane::{LaneBinding}::lane", + "callee": "lance_graph_dt64::lane::{LaneBinding}::check", + "sites": [ + "crates/lance-graph-dt64/src/lane.rs:360" + ], + "fresh": true + } + ], + "possible_edges": [ + { + "dispatch_target": "lance_graph_dt64::lane::LaneSource::lane_of" + } + ], + "co_changed_only": "NOT_RUN (no history projection in this prototype)", + "unsupported_regions": [ + "crates/cognitive-polyfill-probe/tests/p0.rs", + "crates/dir-rmap-probe/tests/equivalence.rs", + "crates/dr-isa-stream-lab/tests/integration/g2l.rs", + "crates/dr-math-binding-probe/tests/binding.rs", + "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs", + "crates/lance-graph-dt64/tests/dr.rs", + "crates/lance-graph-dt64/tests/lane_socket.rs", + "crates/lance-graph-report/src/lane_source/tests.rs", + "crates/thought-git-probe/tests/receipt.rs", + "crates/xlog-relcomp-probe/tests/dto_binding.rs" + ], + "findings": [ + { + "id": "dependents", + "summary": "2 confirmed transitive in-store callers; external callers UNKNOWN", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "confidence": "as graded per item", + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "PARTIAL" + }, + { + "id": "tests", + "summary": "48 candidate test call sites by name; none run", + "severity": null, + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "items": [ + { + "site": "crates/cognitive-polyfill-probe/tests/p0.rs:271", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/cognitive-polyfill-probe/tests/p0.rs:275", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:234", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:238", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:249", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:481", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:522", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:663", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:677", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:678", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dir-rmap-probe/tests/equivalence.rs:680", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:136", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-isa-stream-lab/tests/integration/g2l.rs:263", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/dr-math-binding-probe/tests/binding.rs:234", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:162", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:722", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1012", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dir-sim/tests/ordinal_chain_lab.rs:1328", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:58", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:60", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:72", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:82", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:86", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:89", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:90", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:93", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:776", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:777", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:808", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:814", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:824", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:827", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/dr.rs:828", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:227", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:277", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:314", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:369", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:373", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:403", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:407", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-dt64/tests/lane_socket.rs:424", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: inside a macro region", + "run_status": "NOT_RUN" + }, + { + "site": "crates/thought-git-probe/tests/receipt.rs:715", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/thought-git-probe/tests/receipt.rs:715", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/xlog-relcomp-probe/tests/dto_binding.rs:233", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:320", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:341", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:418", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + }, + { + "site": "crates/lance-graph-report/src/lane_source/tests.rs:623", + "evidence_class": "SOURCE_STRUCTURE", + "qualifier": "POSSIBLE", + "why": "method-name match, receiver unresolved", + "ra": "UNKNOWN: file not indexed", + "run_status": "NOT_RUN" + } + ], + "provenance": { + "tool": "source search + rust-analyzer GRAPH=1 (ra_ap 0.0.358)" + }, + "lifecycle": "open", + "run_status": "NOT_RUN" + }, + { + "id": "contracts", + "summary": "trait methods the symbol dispatches through (a change may interact with any impl)", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "qualifier": "POSSIBLE", + "items": [ + "lance_graph_dt64::lane::LaneSource::lane_of" + ], + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "COMPLETE" + }, + { + "id": "execution_paths", + "summary": "entry points (in-store callers with no in-store caller)", + "severity": null, + "evidence_class": "COMPILER_DERIVED", + "items": [ + "lance_graph_dt64::lane::{LaneBinding}::borrow_all", + "lance_graph_dt64::lane::{LaneBinding}::lane" + ], + "provenance": { + "tool": "charon c69e231 -> ruff_charon import (rows.tsv v2)", + "toolchain": "nightly-2026-09-17", + "sysroot": "default (cargo-miri absent; std bodies opaque)" + }, + "lifecycle": "open", + "run_status": "PARTIAL" + } + ], + "r2il": { + "method": "NOT_A_METHOD", + "body_semantics": "UNKNOWN", + "why": "R2IL describes the effects of a registered DT64 MethodBinding (prepare binds them to opcodes); a Rust function found by the compiler is not one, so its body has no R2IL reading" + }, + "expected_behavior": "NOT_SUPPLIED (must come from intent/tests, independently of this packet)", + "candidate_behavior": "NOT_INFERRED", + "missing_necessary_evidence": [ + "test runs (TEST_OBSERVED needs a run that can fail, CR-H0 \u00a75)", + "compiler artifacts of consumer crates and test targets", + "a resolved receiver for each name-matched test site" + ], + "permission_state": "NOT_DERIVED (never from confidence)" +} diff --git a/.claude/probes/2026-11-11-impact-report-merged/RECONCILIATION.md b/.claude/probes/2026-11-11-impact-report-merged/RECONCILIATION.md new file mode 100644 index 000000000..aa5297de2 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/RECONCILIATION.md @@ -0,0 +1,134 @@ +# Impact report: one API from PR 2001 and PR 2002 + +Ruling: D-CODEGRAPH-BOARD-RULING-2, Wave 0 (Linear EXO-232). Both PRs added +`Codegraph::impact_report` in `crates/lance-graph-codegraph/src/impact_report.rs` with different +types. This branch carries both PRs' commits and one implementation. `Codegraph::impact` is +unchanged in both and here. + +The two reports (`../2026-11-11-crai-report-1/REPORT.md`, `../2026-11-11-codegraph-recon-2/REPORT.md`) +keep the names their PR used. This table maps them. + +## Types + +| PR 2001 | PR 2002 | Merged | +|---|---|---| +| `ExtractionScope<'a>` (borrowed) | `Scope` (owned) | `Scope` (owned) | +| `Targets::{LibraryOnly, LibraryAndTests}`, one value for the whole store | `Targets { lib, tests, examples, benches }` per crate | PR 2002's, per crate | +| `Dependents::{NotDeclared, Declared(&[..])}` | `consumers_closed: bool` | `Consumers::{NotDeclared, Declared(Vec), AllInStore}` | +| — | `excluded: Vec<(crate, regions)>` | kept | +| `ImpactAnswer::{Found(report), SymbolNotInStore}` | `ImpactReport.symbol: SymbolState` | `SymbolState`, plus PR 2001's `observed` crate list on the report | +| `no_known_gap()` | `closed_in_scope()` | `closed_in_scope()` | + +## Gaps + +| PR 2001 `CoverageGap` | PR 2002 `Gap` | Merged `Gap` | +|---|---|---| +| `ConsumersNotDeclared` (symbol public) | `ExternalConsumersUnobserved` (symbol or a caller public) | `ExternalConsumersUnobserved` | +| `ConsumersNotInStore { crates }` | — | kept | +| `TestTargetsNotExtracted` | `TargetsNotExtracted`, `TargetsUndeclared` | PR 2002's | +| — | `RegionsExcluded` | kept | +| `UnresolvedDispatch { possible }` | — | `TraitDispatch { possible }` | +| `DynamicCalls { unknown }` | `UnresolvedDispatch { dynamic_calls }` | `DynamicCalls { unknown, store_dynamic_calls }` | +| `PartialCrate`, `Residuals`, `ResidualsNotRead` | `HarvestIncomplete` | `HarvestIncomplete` | +| `StaleSource`, `MissingSource` (whole store) | `StaleSource` (per crate; a vanished file counted as stale) | both, per crate | +| — | `SourceFilesUnread` | kept | +| `NoFileList` | `FilesUnrecorded` | `FilesUnrecorded` | +| `InvalidFileList` | — (a short row counted as stale) | `InvalidFileList`, also for a table without `#schema` | +| `FreshnessNotChecked` | `FreshnessUnchecked` | `FreshnessUnchecked` | + +## Behaviour taken from each + +From PR 2001: +- A symbol whose rows a refresh removed is `Missing` (`has_rows`), not found with an empty answer. +- A crate read without a CRATE row is listed and counts as partial. +- Declared consumers are named, so a consumer missing from the store is reported by name. +- `files.tsv` is read by its `#schema` columns; the digest is `lance_graph_contract::hash::fnv1a`. + +From PR 2002: +- Targets are declared per crate; an undeclared crate is a gap. +- Excluded regions and source files the extractor never read are gaps. +- The external-consumer gap also fires when a caller of the symbol is public. +- A missing symbol in a stored crate carries that crate's target, region, harvest and file gaps. +- Harvest and file gaps are reported for the crates the answer depends on, not for every crate + in the store. + +Decided in the merge, in neither PR as such: +- The crates the answer depends on are the owner, every stored crate with a function in the + answer, and the stored crates that can be consumers. Those are the declared ones + (`Declared`), or every stored crate when the consumers are not named (`NotDeclared`, + `AllInStore`) and the symbol or one of its callers is public. PR 2002 checked every stored + crate only under `consumers_closed`; with undeclared consumers a stale stored crate that could + call the symbol was not reported. +- PR 2002's dynamic-call gap for an empty `graded.unknown` is KEPT (symbol is an impl or is + referenced, and the store holds a dynamic call). The first merged commit dropped it on the + argument that `impact` then already lists every function holding a dynamic call. That argument + is about positions only: a caller that is confirmed through a direct call says nothing about + where its dynamic call goes. Restored after the operator's review, with a fixture that isolates + the case (`a_dynamic_call_from_an_already_confirmed_caller_is_still_a_gap`): the gap is + `DynamicCalls { unknown: 0, store_dynamic_calls: 1 }`. The earlier note "no fixture store + produced the case" covered only the five committed stores; a four-row synthetic store does. +- `ReferrersOutsideAnswer { referrers }` is new, from the PR 2002 session's acceptance review + (PR 2004, D-CODEGRAPH-RECON-ACCEPT, finding 2b). A function that takes the address of the + symbol, or of a function in the answer, without calling it is in no mask, because a + `REFERENCES` row is not a call edge. Both PRs then reported "closed"; PR 2002's dynamic-call + half hid the hole only when the store happened to hold a dynamic call. The gap counts the + distinct referrers outside the answer and needs no dynamic call. On the report-rs store it + fires for `prefs::Layout::label` (`frame::tools_html` references it and never calls it). +- `TraitDispatch` (`graded.possible`) and `DynamicCalls` (`graded.unknown` or a reachable + dynamic call) stay two gaps. Neither PR had both. + +## CLI + +`cg_query impact-report SYMBOL [--deps a,b | --consumers-closed] +[--targets CRATE=lib,tests,examples,benches]... [--exclude CRATE=REGION]... [--no-freshness]` + +- `report` is kept as an alias (PR 2002's name). +- `--targets '*=LIST'` declares every stored crate. PR 2001's `--with-tests` is gone; a crate + without `--targets` is reported as undeclared instead of assumed library-only. +- Output lines: `symbol`, `impact`, `observed`, `GAP`, `closed_in_scope`. A missing symbol exits 4. +- PR 2002's recorded `store-output/*.report` files are regenerated in `report-rs-33/`; see + its README for the line-by-line difference. + +## Tests + +`tests/impact_coverage.rs` holds 37 tests on the merged types: + +- 12 from PR 2001 and 14 from PR 2002, each with its original intent. +- 3 narrowing tests: adding a declared consumer, declaring test targets, and removing an + excluded region each remove their gap. +- 3 added after the disable runs showed a guard without a test: a deleted source file is + `MissingSource` and not `StaleSource`; a declared consumer with no caller in the answer still + needs its own target declaration; a dynamic call from an already confirmed caller is still a + gap. +- 4 from the acceptance review: a function that only takes the address is a gap, with and + without a dynamic call in the store; a referrer that also calls is in the answer and no gap; + only references into the answer are counted; undeclared consumers inspect every stored crate + and `graded` stays equal to `impact`. +- 1 from the Codex review of PR 2004: a dynamic call that may dispatch to a caller of the + symbol (an impl) is a gap even when the symbol itself is neither an impl nor referenced. + `dynamic_may_reach` checked the symbol only; `impact` checks the symbol and what reaches it. + +One PR 2001 test changed what it queries. `a_changed_source_file_reports_stale_coverage` edited +a `cg_lib` file and asked about `cg_app::run` with "nothing depends on `cg_app`" declared. Under +the scoped rule that edit cannot move the answer, so the test now asks about a `cg_lib` symbol, +asserts the declared-scope case reports nothing for `cg_app::run`, and asserts the gap returns +when the consumers are undeclared. + +Disable runs are reproducible: `python3 .claude/probes/2026-11-11-impact-report-merged/disable_runs.py` +from the repository root, on a clean tree. It patches each of 28 guards out through an anchor +that must occur exactly once, runs the test file, restores the source, and exits 0 only when +every guard is red. The last run is `disable_runs.receipt.tsv` (28 guards, 0 not red), which +names the tests each guard breaks. + +## Acceptance state (D-CODEGRAPH-WAVE0-ACCEPTANCE) + +- PR 2002's report-rs outputs against the merged API: regenerated from a fresh extraction of + report-rs `81705d9`, in `report-rs-33/`. Four of six files are identical to PR 2002's; one has + the renamed dynamic-call line; one gains the harvest gap PR 2002's own later test requires. +- `member-tests` timeout: not caused by this change. On `main` the job took 19.7, 20.5, 21.1, + 26.4 and 29.8 minutes over five runs against `timeout-minutes: 30`. PR 2001's head hit the + limit twice (30.3 and 30.2 minutes) with every step slower than on `main`, not one outlier; an + earlier head of the same PR passed in 24.7 and PR 2002 passed in 25.6. The job runs about + twenty `cargo test --manifest-path` steps, and the hydrate and surreal_container steps each + compile the arrow / datafusion / lance stack again. Raising the limit or splitting those steps + is a CI change for its own PR. diff --git a/.claude/probes/2026-11-11-impact-report-merged/disable_runs.py b/.claude/probes/2026-11-11-impact-report-merged/disable_runs.py new file mode 100644 index 000000000..6646ddcc4 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/disable_runs.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Disable runs for crates/lance-graph-codegraph/src/impact_report.rs. + +Each guard is patched out (the anchor must occur exactly once, or the run is reported as +ANCHOR-MISSING and counts as a failure), `cargo test --release --test impact_coverage` is run, and +the file is restored. A guard is load-bearing when its run is RED. + +Run from the repository root on a clean tree: python3 +Exit 0 only when every guard is RED. Writes nothing but the temporary patch. +""" +import re, subprocess, sys +from pathlib import Path + +CRATE = Path(__file__).resolve().parents[3] / "crates/lance-graph-codegraph" +P = CRATE / "src/impact_report.rs" +TEST = "impact_coverage" +G=[ + ("has_rows", ".filter(|_| self.has_rows(symbol))", ""), + ("undeclared-involves-all", " // Any stored crate may be a consumer too, so each one's coverage counts.\n involved.extend(stored.iter().map(|k| (*k).to_owned()));\n", ""), + ("consumers-not-in-store", "if !absent.is_empty() {", "if false {"), + ("declared-consumer-involved", " involved.insert(d.clone());", " let _ = d;"), + ("targets-not-extracted", "if missing.any() {", "if false {"), + ("targets-undeclared", " None => gaps.push(Gap::TargetsUndeclared {\n krate: k.to_owned(),\n }),", " None => {}"), + ("regions-excluded", ".find(|(n, r)| n == k && !r.is_empty())", ".find(|(n, r)| n == k && r.is_empty() && !r.is_empty())"), + ("trait-dispatch", "if possible > 0 {", "if false {"), + ("dynamic-unknown", "if unknown > 0 || (store_dynamic_calls > 0 && self.dynamic_may_reach(g, &graded)) {", "if store_dynamic_calls > 0 && self.dynamic_may_reach(g, &graded) && unknown > u32::MAX - 1 {"), + ("dynamic-reach-through-callers", " .any(target)\n", " .any(|_| false)\n"), + ("dynamic-may-reach", "if unknown > 0 || (store_dynamic_calls > 0 && self.dynamic_may_reach(g, &graded)) {", "if unknown > 0 {"), + ("public-caller-frontier", ".any(|m| m.iter().zip(self.public()).any(|(a, b)| a & b != 0));", ".any(|m| m.is_empty() && !m.is_empty());"), + ("public-symbol-frontier", "let frontier_public = has(self.public(), g)\n ||", "let frontier_public = false\n ||"), + ("all-in-store", " Consumers::AllInStore => {\n if frontier_public {", " Consumers::AllInStore => {\n if false {"), + ("harvest-residuals", "if residuals.total() > 0 || no_residuals_file || partial {", "if no_residuals_file || partial {"), + ("harvest-no-file", "if residuals.total() > 0 || no_residuals_file || partial {", "if residuals.total() > 0 || partial {"), + ("harvest-partial", "if residuals.total() > 0 || no_residuals_file || partial {", "if residuals.total() > 0 || no_residuals_file {"), + ("missing-symbol-gaps", " if owner_stored {\n scope_gaps(&owner, scope, &mut gaps);", " if false {\n scope_gaps(&owner, scope, &mut gaps);"), + ("stale", "if !f.stale.is_empty() {", "if false {"), + ("missing-file", "if !f.missing.is_empty() {", "if false {"), + ("unread", "if !f.unread.is_empty() {", "if false {"), + ("invalid-row", " let (Some(path), Some(want)) = (fields.get(path_col), fields.get(hash_col)) else {\n out.invalid = true;", " let (Some(path), Some(want)) = (fields.get(path_col), fields.get(hash_col)) else {\n out.invalid = false;"), + ("files-unrecorded", "gaps.push(Gap::FilesUnrecorded { krate: k.clone() });", "let _ = k;"), + ("referrers-gap", " if referrers > 0 {", " if false {"), + ("referrers-only-outside", " if object_in && !subject_in {", " if object_in {"), + ("referrers-only-into-answer", " if object_in && !subject_in {", " if !subject_in {"), + ("referrers-only-references", " if l.pred[i] != P_REFERENCES {\n continue;\n }\n", ""), + ("freshness-unchecked", " gaps.push(Gap::FreshnessUnchecked);\n return;", " return;"), +] + +def main(): + if subprocess.run(["git", "diff", "--quiet", "--", str(P)]).returncode != 0: + sys.exit(f"{P} has local changes; commit first (the restore would not be exact)") + orig = P.read_text() + bad = 0 + try: + for name, old, new in G: + if orig.count(old) != 1: + print(f"{name}\tANCHOR-MISSING({orig.count(old)})") + bad += 1 + continue + P.write_text(orig.replace(old, new)) + out = subprocess.run( + ["cargo", "test", "--release", "--test", TEST], + cwd=CRATE, capture_output=True, text=True, + ) + text = out.stdout + out.stderr + m = re.search(r"test result: (\w+)\. (\d+) passed; (\d+) failed", text) + failed = sorted(set(re.findall(r"^test (\S+) \.\.\. FAILED", text, re.M))) + if not m: + print(f"{name}\tBUILD-ERROR") + bad += 1 + elif m.group(1) == "FAILED": + print(f"{name}\tRED\t{','.join(f.split('::')[-1] for f in failed)}") + else: + print(f"{name}\tGREEN\tno test depends on this guard") + bad += 1 + finally: + P.write_text(orig) + print(f"guards\t{len(G)}\tnot-red\t{bad}") + sys.exit(1 if bad else 0) + + +if __name__ == "__main__": + main() diff --git a/.claude/probes/2026-11-11-impact-report-merged/disable_runs.receipt.tsv b/.claude/probes/2026-11-11-impact-report-merged/disable_runs.receipt.tsv new file mode 100644 index 000000000..4760ad725 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/disable_runs.receipt.tsv @@ -0,0 +1,29 @@ +has_rows RED a_symbol_removed_by_a_refresh_is_not_found +undeclared-involves-all RED a_changed_source_file_reports_stale_coverage,undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact +consumers-not-in-store RED adding_a_declared_consumer_narrows_the_gap,a_public_symbol_with_unobserved_consumers_reports_missing_coverage,adding_the_consumer_crate_upgrades_missing_coverage_to_confirmed +declared-consumer-involved RED a_declared_consumer_without_a_caller_still_needs_its_own_coverage +targets-not-extracted RED declaring_test_targets_narrows_the_gap,library_only_extraction_reports_unobserved_test_callers,a_missing_symbol_carries_its_crate_target_gaps,test_callers_absent_from_a_lib_extraction_are_a_gap,undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact +targets-undeclared RED a_declared_consumer_without_a_caller_still_needs_its_own_coverage,adding_a_consumer_crate_confirms_its_callers,test_callers_absent_from_a_lib_extraction_are_a_gap +regions-excluded RED removing_an_excluded_region_narrows_the_gap,a_closed_consumer_set_checks_consumers_without_observed_edges,excluded_regions_are_a_gap_for_found_and_missing_symbols,undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact +trait-dispatch RED dispatch_only_callers_report_unresolved_dispatch +dynamic-unknown RED a_dynamic_call_from_an_already_confirmed_caller_is_still_a_gap,a_dynamic_call_that_may_reach_a_caller_of_the_symbol_is_a_gap,an_unresolved_dispatch_that_may_reach_the_symbol_is_a_gap +dynamic-reach-through-callers RED a_dynamic_call_that_may_reach_a_caller_of_the_symbol_is_a_gap +dynamic-may-reach RED a_dynamic_call_from_an_already_confirmed_caller_is_still_a_gap,a_dynamic_call_that_may_reach_a_caller_of_the_symbol_is_a_gap +public-caller-frontier RED a_private_symbol_behind_a_public_caller_has_unobserved_consumers +public-symbol-frontier RED a_changed_source_file_reports_stale_coverage,a_closed_consumer_set_checks_consumers_without_observed_edges,a_public_symbol_reports_its_unobserved_external_consumers,adding_a_consumer_crate_confirms_its_callers,undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact +all-in-store RED a_closed_consumer_set_checks_consumers_without_observed_edges +harvest-residuals RED extraction_residuals_are_a_coverage_gap,a_missing_symbol_carries_its_crate_harvest_gap,an_incomplete_harvest_of_the_owner_is_a_gap +harvest-no-file RED a_missing_residuals_file_is_a_coverage_gap,an_incomplete_harvest_of_the_owner_is_a_gap +harvest-partial RED a_crate_without_a_crate_row_is_partial_not_absent +missing-symbol-gaps RED a_missing_symbol_carries_its_crate_harvest_gap,a_missing_symbol_carries_its_crate_target_gaps,excluded_regions_are_a_gap_for_found_and_missing_symbols,unread_source_files_under_a_read_directory_are_a_gap +stale RED a_changed_source_file_reports_stale_coverage,a_stale_source_revision_is_a_gap,undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact +missing-file RED a_deleted_source_file_is_missing_not_stale +unread RED unread_source_files_under_a_read_directory_are_a_gap +invalid-row RED a_malformed_file_list_row_is_a_freshness_gap +files-unrecorded RED a_stale_source_revision_is_a_gap +referrers-gap RED a_function_that_only_takes_the_address_is_a_gap,only_references_into_the_answer_are_counted +referrers-only-outside RED a_referrer_that_also_calls_is_in_the_answer_and_no_gap +referrers-only-into-answer RED only_references_into_the_answer_are_counted +referrers-only-references RED only_references_into_the_answer_are_counted +freshness-unchecked RED a_changed_source_file_reports_stale_coverage,a_stale_source_revision_is_a_gap +guards 28 not-red 0 diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/README.md b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/README.md new file mode 100644 index 000000000..f4b5993bf --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/README.md @@ -0,0 +1,41 @@ +# PR 2002's report-rs outputs, regenerated with the merged API + +Store: report-rs `81705d9` (the PR 33 head), re-extracted in this session with Charon `c69e231` +(nightly-2026-09-17, library target, default features, `--opaque crate::app`) and ruff_charon. +The import reported `harvested=29542 represented=29541 residual=1`, the numbers PR 2002 recorded. +The store is not committed (6 MB of rows); these six files are its output. + +Command, for each symbol on the first line of the matching file in +`../../2026-11-11-codegraph-recon-2/store-output/`: + +``` +cg_query impact-report SYMBOL --targets report_rs=lib \ + --exclude report_rs=opaque:crate::app \ + --exclude report_rs=features-off:lance,iam,flow,oidc,projects,projects-pg +``` + +The flags were not recorded by PR 2002. They are read back from its `GAP` lines, and the result +below is the check that they are the right ones. + +## Difference from PR 2002's recorded files + +Line by line, after removing the new `observed` line: + +| file | difference | +|---|---| +| `pull.report` | one line renamed: `UnresolvedDispatch { dynamic_calls: 17 }` is now `DynamicCalls { unknown: 101, store_dynamic_calls: 17 }`. All 102 `impact` lines and the five other gaps are identical. | +| `poll.report` | none | +| `srcpull.report` | none | +| `missing.authorize_memberships.report` | none | +| `missing.maplog.report` | none | +| `missing.report_rs_iam_authorize.report` | one line added: `HarvestIncomplete` for `report_rs` (1 residual). PR 2002's own later test `a_missing_symbol_carries_its_crate_harvest_gap` requires it; its recorded file predates that. | + +None of the six symbols has a referrer outside its answer, so the `ReferrersOutsideAnswer` gap +added after the acceptance review does not appear in them. It does fire on this store for +`report_rs::prefs::{report_rs::prefs::Layout}::label` (`referrers: 1`). + +Every file gains one `observed report_rs Complete` line (the stored-crate list from PR 2001). A +missing symbol now exits 4. + +The `callers`, `callees` and `refs` files of PR 2002 come from queries this merge did not change +and are not regenerated. diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.authorize_memberships.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.authorize_memberships.report new file mode 100644 index 000000000..95de6705d --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.authorize_memberships.report @@ -0,0 +1,3 @@ +symbol MISSING owner_stored=false lance_graph_rbac::authorize::authorize_memberships +observed report_rs Complete +closed_in_scope false diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.maplog.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.maplog.report new file mode 100644 index 000000000..f2ad89b30 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.maplog.report @@ -0,0 +1,3 @@ +symbol MISSING owner_stored=false lance_graph_dt64_lance::MapLog +observed report_rs Complete +closed_in_scope false diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.report_rs_iam_authorize.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.report_rs_iam_authorize.report new file mode 100644 index 000000000..14a5a9732 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/missing.report_rs_iam_authorize.report @@ -0,0 +1,7 @@ +symbol MISSING owner_stored=true report_rs::iam::authorize +observed report_rs Complete +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/poll.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/poll.report new file mode 100644 index 000000000..e5ca18c90 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/poll.report @@ -0,0 +1,8 @@ +symbol FOUND 3615 report_rs::workbook_delta::source::{report_rs::workbook_delta::source::WorkbookSource[TraitClause0, TraitClause1]}::poll +observed report_rs Complete +GAP ExternalConsumersUnobserved { owner: "report_rs", observed_consumers: [] } +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/pull.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/pull.report new file mode 100644 index 000000000..7d32e945f --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/pull.report @@ -0,0 +1,111 @@ +symbol FOUND 1717 report_rs::source_fake::impl_Source_for_WorkbookSource::pull +impact CONFIRMED report_rs::source_fake::impl_Source_for_WorkbookSource::pull::{vtable_method} +impact UNKNOWN report_rs::cli::run +impact UNKNOWN report_rs::cli::run::{impl FnOnce<(S3Client,)> for report_rs::cli::run::{closure#3}<'_0>}::call_once +impact UNKNOWN report_rs::cli::store +impact UNKNOWN report_rs::fixture::{ReportType}::within::{impl FnMut<(&'_1 &'_2 str,)> for report_rs::fixture::{ReportType}::within::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::fixture::{ReportType}::within::{impl FnOnce<(&'_1 &'_2 str,)> for report_rs::fixture::{ReportType}::within::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::lance_adapter::parity +impact UNKNOWN report_rs::mapping_hydrate::hydrate +impact UNKNOWN report_rs::mapping_hydrate::hydrate_artifact +impact UNKNOWN report_rs::mapping_hydrate::hydrate_artifact::{impl FnOnce<(Pointer,)> for report_rs::mapping_hydrate::hydrate_artifact::{closure}<'_0, '_1, T>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::hydrate_from_env +impact UNKNOWN report_rs::mapping_hydrate::hydrate_kind_from_env +impact UNKNOWN report_rs::mapping_hydrate::publish +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl Fn<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl FnMut<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::publish_artifact::{impl FnOnce<(&'_2 str, &'_3 [u8], &'_4 str)> for report_rs::mapping_hydrate::publish_artifact::{closure}<'_0, '_1>}::call_once +impact UNKNOWN report_rs::mapping_hydrate::repoint +impact UNKNOWN report_rs::mapping_hydrate::store_from_env +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::fetch_manifest +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::load_local +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl Fn<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl FnMut<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{impl FnOnce<()> for report_rs::mapping_hydrate::{Ctx<'_0, T>[TraitClause0]}::publish_local::{closure}<'_0, '_1, '_2, '_3, '_4, '_5, '_6, T>[TraitClause0]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl Fn<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnMut<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnOnce<(&'_2 str,)> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{impl FnOnce<()> for report_rs::mapping_hydrate::{HydrateConfig}::from_vars_for::{closure#1}<'_0, '_1, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::mobile::render +impact UNKNOWN report_rs::objstore::{S3Config}::from_suffixed_env +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl Fn<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl FnMut<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::objstore::{S3Config}::from_vars::{impl FnOnce<(&'_3 str,)> for report_rs::objstore::{S3Config}::from_vars::{closure}<'_0, '_1, '_2, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::oracle::summary +impact UNKNOWN report_rs::oracle::summary_where +impact UNKNOWN report_rs::oracle::summary_where::{closure}::{impl FnMut<(&'_1 &'_2 report_rs::fixture::Customer,)> for report_rs::oracle::summary_where::{closure}::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::oracle::summary_where::{closure}::{impl FnOnce<(&'_1 &'_2 report_rs::fixture::Customer,)> for report_rs::oracle::summary_where::{closure}::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::projection::labels_for +impact UNKNOWN report_rs::projection::{Projection}::load_v1 +impact UNKNOWN report_rs::projection::{Projection}::parity +impact UNKNOWN report_rs::projection::{Projection}::summary +impact UNKNOWN report_rs::rules::each_owed +impact UNKNOWN report_rs::rules::impl_Rule_for_ConfiguredButNoRun::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_ConfiguredButNoRun::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_DeliveredWithoutEvidence::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_DeliveredWithoutEvidence::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_EvidenceStale::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_EvidenceStale::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_OwedButNotConfigured::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_OwedButNotConfigured::propose::{vtable_method} +impact UNKNOWN report_rs::rules::impl_Rule_for_RunFailed::propose +impact UNKNOWN report_rs::rules::impl_Rule_for_RunFailed::propose::{vtable_method} +impact UNKNOWN report_rs::rules::plan::{impl FnMut<(&'_2 Box<(dyn Rule + '_3), Global>[{built_in impl MetaSized for (dyn Rule + '_3)}, {built_in impl Sized for Global}, impl_Allocator_for_Global],)> for report_rs::rules::plan::{closure}<'_0, '_1>}::call_mut +impact UNKNOWN report_rs::rules::plan::{impl FnOnce<(&'_2 Box<(dyn Rule + '_3), Global>[{built_in impl MetaSized for (dyn Rule + '_3)}, {built_in impl Sized for Global}, impl_Allocator_for_Global],)> for report_rs::rules::plan::{closure}<'_0, '_1>}::call_once +impact UNKNOWN report_rs::rules::validate::{impl FnMut<(&'_6 Change,)> for report_rs::rules::validate::{closure#1}<'_0, '_1, '_2, '_3, '_4, '_5, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::rules::validate::{impl FnOnce<(&'_6 Change,)> for report_rs::rules::validate::{closure#1}<'_0, '_1, '_2, '_3, '_4, '_5, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnMut<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#2}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnMut<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#3}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnOnce<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#2}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::decide_with_fallback::{impl FnOnce<(&'_1 String,)> for report_rs::scope::decide_with_fallback::{closure#3}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::scope::{ActorSource}::from_env +impact UNKNOWN report_rs::scope::{ActorSource}::from_vars +impact UNKNOWN report_rs::scope::{StaticScope}::from_env +impact UNKNOWN report_rs::scope::{StaticScope}::from_vars +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::pull +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 (report_rs::source::SourceId, IngestBatchId), &'_2 mut IngestBatch)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 SourceFailure,)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#1}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut BTreeSet[{built_in impl Sized for ObservationLane}, {built_in impl Sized for Global}, impl_AllocatorClone_for_Global])> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#2}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut SourceScope)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#3}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnMut<(&'_1 report_rs::source::SourceId, &'_2 mut String)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#4}<'_0, T0>[TraitClause0, TraitClause1]}::call_mut +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 (report_rs::source::SourceId, IngestBatchId), &'_2 mut IngestBatch)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 SourceFailure,)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#1}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut BTreeSet[{built_in impl Sized for ObservationLane}, {built_in impl Sized for Global}, impl_AllocatorClone_for_Global])> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#2}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut SourceScope)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#3}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source::{report_rs::source::Ledger}::retain_sources::{impl FnOnce<(&'_1 report_rs::source::SourceId, &'_2 mut String)> for report_rs::source::{report_rs::source::Ledger}::retain_sources::{closure#4}<'_0, T0>[TraitClause0, TraitClause1]}::call_once +impact UNKNOWN report_rs::source_graph::pull_into +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_env +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl Fn<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl FnMut<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::source_graph::{GraphConfig}::from_vars::{impl FnOnce<(&'_1 str,)> for report_rs::source_graph::{GraphConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::source_graph::{GraphConfig}::lab_from_vars +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl Fn<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl FnMut<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{impl FnOnce<(&'_4 Tool, &'_5 str)> for report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_addressed::{closure}<'_0, '_1, '_2, '_3, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::rail_html_with +impact UNKNOWN report_rs::surface_fixture::{report_rs::surface_fixture::Surface}::tools_html +impact UNKNOWN report_rs::view::project::{impl FnMut<(&'_1 report_rs::view::Column,)> for report_rs::view::project::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::view::project::{impl FnOnce<(&'_1 report_rs::view::Column,)> for report_rs::view::project::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::view::{Actor}::from_vars +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl Fn<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl FnMut<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_mut +impact UNKNOWN report_rs::workbook_delta::source::{DriveConfig}::from_vars::{impl FnOnce<(&'_1 str,)> for report_rs::workbook_delta::source::{DriveConfig}::from_vars::{closure}<'_0, T0>[TraitClause0, for<'_0_1> TraitClause1<'_>]}::call_once +impact UNKNOWN report_rs::xlsx::export +impact UNKNOWN report_rs::xlsx::workbook +observed report_rs Complete +GAP ExternalConsumersUnobserved { owner: "report_rs", observed_consumers: [] } +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP DynamicCalls { unknown: 101, store_dynamic_calls: 17 } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/srcpull.report b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/srcpull.report new file mode 100644 index 000000000..310324202 --- /dev/null +++ b/.claude/probes/2026-11-11-impact-report-merged/report-rs-33/srcpull.report @@ -0,0 +1,7 @@ +symbol FOUND 1718 report_rs::source::Source::pull +observed report_rs Complete +GAP TargetsNotExtracted { krate: "report_rs", missing: Targets { lib: false, tests: true, examples: true, benches: true } } +GAP RegionsExcluded { krate: "report_rs", regions: ["opaque:crate::app", "features-off:lance,iam,flow,oidc,projects,projects-pg"] } +GAP HarvestIncomplete { krate: "report_rs", residuals: ResidualCounts([0, 0, 1, 0, 0]), partial: false, no_residuals_file: false } +GAP SourceFilesUnread { krate: "report_rs", files: ["src/app.rs", "src/app/plan_routes.rs", "src/flow_scratch.rs", "src/iam.rs", "src/login.rs", "src/main.rs", "src/project_plan/calendar.rs", "src/project_plan/engine.rs", "src/project_plan/mod.rs", "src/project_plan/pg.rs", "src/project_plan/projection.rs", "src/project_plan/store.rs", "src/project_plan/view.rs", "src/workbook_delta/dt64/p21_lane_socket_lab.rs"] } +closed_in_scope false diff --git a/.codegraph/coverage.tsv b/.codegraph/coverage.tsv index c2e4302d6..a44ee53b2 100644 --- a/.codegraph/coverage.tsv +++ b/.codegraph/coverage.tsv @@ -36,7 +36,7 @@ lance-graph-benches 0 10 0 macro-generated items invisible;fn-local use not scan lance-graph-callcenter 35 6 3 macro-generated items invisible;fn-local use not scanned lance-graph-catalog 9 1 0 macro-generated items invisible;fn-local use not scanned lance-graph-codec-research 10 0 0 macro-generated items invisible;fn-local use not scanned -lance-graph-codegraph 8 11 0 macro-generated items invisible;fn-local use not scanned +lance-graph-codegraph 9 12 0 macro-generated items invisible;fn-local use not scanned lance-graph-cognitive 60 0 0 macro-generated items invisible;fn-local use not scanned lance-graph-consumer-conformance 2 0 0 macro-generated items invisible;fn-local use not scanned lance-graph-contract 175 35 2 macro-generated items invisible;fn-local use not scanned diff --git a/.codegraph/crates.tsv b/.codegraph/crates.tsv index 9c2335869..3d9b0d427 100644 --- a/.codegraph/crates.tsv +++ b/.codegraph/crates.tsv @@ -36,7 +36,7 @@ lance-graph-benches crates/lance-graph-benches member 2f2fb296a08260fb lance-graph-callcenter crates/lance-graph-callcenter member 41a65d38ac70bc87 lance-graph-catalog crates/lance-graph-catalog member 9d0c34f70c28a465 lance-graph-codec-research crates/lance-graph-codec-research excluded 8b8afbd9a6d0f359 -lance-graph-codegraph crates/lance-graph-codegraph excluded bd209bed318ffbbc +lance-graph-codegraph crates/lance-graph-codegraph excluded 434f8b1c7314d3b0 lance-graph-cognitive crates/lance-graph-cognitive excluded 93f956ac0d69ca66 lance-graph-consumer-conformance crates/lance-graph-consumer-conformance member 82e980465d770990 lance-graph-contract crates/lance-graph-contract member 6b4e13ff1d0afadb diff --git a/.codegraph/crates/lance-graph-codegraph.index.md b/.codegraph/crates/lance-graph-codegraph.index.md index 73728ed9c..10c90acb4 100644 --- a/.codegraph/crates/lance-graph-codegraph.index.md +++ b/.codegraph/crates/lance-graph-codegraph.index.md @@ -3,20 +3,20 @@ crate: lance-graph-codegraph path: crates/lance-graph-codegraph workspace: excluded -digest: bd209bed318ffbbc +digest: 434f8b1c7314d3b0 evidence legend: ../index.md#evidence-legend Generated; the code is the truth. See ../index.md. ## Depends on -- dep lance-graph-contract dev — crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED +- dep lance-graph-contract normal — crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED - dep lance-graph-dt64 normal — crates/lance-graph-codegraph/Cargo.toml:13 CONFIRMED - dep lance-graph-mask-risc normal — crates/lance-graph-codegraph/Cargo.toml:15 CONFIRMED -- dep lance-graph-planner dev — crates/lance-graph-codegraph/Cargo.toml:22 CONFIRMED +- dep lance-graph-planner dev — crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED - dep lance-graph-quack normal — crates/lance-graph-codegraph/Cargo.toml:14 CONFIRMED - dep lance-graph-xlog normal — crates/lance-graph-codegraph/Cargo.toml:12 CONFIRMED -- dep ndarray normal — crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED +- dep ndarray normal — crates/lance-graph-codegraph/Cargo.toml:17 CONFIRMED - dep tokio dev — crates/lance-graph-codegraph/Cargo.toml:24 CONFIRMED ## Depended on by @@ -174,26 +174,39 @@ Generated; the code is the truth. See ../index.md. - fn Codegraph::any_partial [pub(crate)] — crates/lance-graph-codegraph/src/graph.rs:1009 CONFIRMED - fn Codegraph::words [pub] — crates/lance-graph-codegraph/src/graph.rs:1014 CONFIRMED - fn Codegraph::mask_of [pub] — crates/lance-graph-codegraph/src/graph.rs:1019 CONFIRMED -- fn Codegraph::crate_status [pub] — crates/lance-graph-codegraph/src/graph.rs:1030 CONFIRMED -- fn Codegraph::rows [pub] — crates/lance-graph-codegraph/src/graph.rs:1036 CONFIRMED -- fn Codegraph::row_predicate [pub] — crates/lance-graph-codegraph/src/graph.rs:1041 CONFIRMED -- fn Codegraph::row_subject [pub] — crates/lance-graph-codegraph/src/graph.rs:1046 CONFIRMED -- fn Codegraph::row_object [pub] — crates/lance-graph-codegraph/src/graph.rs:1051 CONFIRMED -- fn Codegraph::site [pub] — crates/lance-graph-codegraph/src/graph.rs:1056 CONFIRMED -- fn Codegraph::cube [pub] — crates/lance-graph-codegraph/src/graph.rs:1070 CONFIRMED -- fn Codegraph::cube_mut [pub] — crates/lance-graph-codegraph/src/graph.rs:1075 CONFIRMED -- fn Codegraph::cube_and_table_mut [pub(crate)] — crates/lance-graph-codegraph/src/graph.rs:1081 CONFIRMED -- fn Codegraph::table [pub] — crates/lance-graph-codegraph/src/graph.rs:1086 CONFIRMED -- fn Codegraph::fns [pub] — crates/lance-graph-codegraph/src/graph.rs:1091 CONFIRMED -- fn Codegraph::public [pub] — crates/lance-graph-codegraph/src/graph.rs:1095 CONFIRMED -- fn Codegraph::impl_subjects [pub] — crates/lance-graph-codegraph/src/graph.rs:1099 CONFIRMED -- fn Codegraph::synthetic [pub] — crates/lance-graph-codegraph/src/graph.rs:1103 CONFIRMED -- fn Codegraph::referenced [pub] — crates/lance-graph-codegraph/src/graph.rs:1107 CONFIRMED -- fn Codegraph::called [pub] — crates/lance-graph-codegraph/src/graph.rs:1111 CONFIRMED -- fn Codegraph::may_targets [pub] — crates/lance-graph-codegraph/src/graph.rs:1115 CONFIRMED -- fn Codegraph::dynamic_callers [pub] — crates/lance-graph-codegraph/src/graph.rs:1119 CONFIRMED -- fn Codegraph::stored_owner [pub] — crates/lance-graph-codegraph/src/graph.rs:1123 CONFIRMED -- fn Codegraph::closure [pub] — crates/lance-graph-codegraph/src/graph.rs:1129 CONFIRMED +- fn Codegraph::crate_list [pub] — crates/lance-graph-codegraph/src/graph.rs:1031 CONFIRMED +- fn Codegraph::has_rows [pub(crate)] — crates/lance-graph-codegraph/src/graph.rs:1052 CONFIRMED +- fn Codegraph::crate_residuals [pub] — crates/lance-graph-codegraph/src/graph.rs:1062 CONFIRMED +- fn Codegraph::crate_status [pub] — crates/lance-graph-codegraph/src/graph.rs:1067 CONFIRMED +- fn Codegraph::rows [pub] — crates/lance-graph-codegraph/src/graph.rs:1073 CONFIRMED +- fn Codegraph::row_predicate [pub] — crates/lance-graph-codegraph/src/graph.rs:1078 CONFIRMED +- fn Codegraph::row_subject [pub] — crates/lance-graph-codegraph/src/graph.rs:1083 CONFIRMED +- fn Codegraph::row_object [pub] — crates/lance-graph-codegraph/src/graph.rs:1088 CONFIRMED +- fn Codegraph::site [pub] — crates/lance-graph-codegraph/src/graph.rs:1093 CONFIRMED +- fn Codegraph::cube [pub] — crates/lance-graph-codegraph/src/graph.rs:1107 CONFIRMED +- fn Codegraph::cube_mut [pub] — crates/lance-graph-codegraph/src/graph.rs:1112 CONFIRMED +- fn Codegraph::cube_and_table_mut [pub(crate)] — crates/lance-graph-codegraph/src/graph.rs:1118 CONFIRMED +- fn Codegraph::table [pub] — crates/lance-graph-codegraph/src/graph.rs:1123 CONFIRMED +- fn Codegraph::fns [pub] — crates/lance-graph-codegraph/src/graph.rs:1128 CONFIRMED +- fn Codegraph::public [pub] — crates/lance-graph-codegraph/src/graph.rs:1132 CONFIRMED +- fn Codegraph::impl_subjects [pub] — crates/lance-graph-codegraph/src/graph.rs:1136 CONFIRMED +- fn Codegraph::synthetic [pub] — crates/lance-graph-codegraph/src/graph.rs:1140 CONFIRMED +- fn Codegraph::referenced [pub] — crates/lance-graph-codegraph/src/graph.rs:1144 CONFIRMED +- fn Codegraph::called [pub] — crates/lance-graph-codegraph/src/graph.rs:1148 CONFIRMED +- fn Codegraph::may_targets [pub] — crates/lance-graph-codegraph/src/graph.rs:1152 CONFIRMED +- fn Codegraph::dynamic_callers [pub] — crates/lance-graph-codegraph/src/graph.rs:1156 CONFIRMED +- fn Codegraph::stored_owner [pub] — crates/lance-graph-codegraph/src/graph.rs:1160 CONFIRMED +- fn Codegraph::closure [pub] — crates/lance-graph-codegraph/src/graph.rs:1166 CONFIRMED +- struct Targets [pub] — crates/lance-graph-codegraph/src/impact_report.rs:31 CONFIRMED +- const Targets::LIB_ONLY [pub] — crates/lance-graph-codegraph/src/impact_report.rs:44 CONFIRMED +- const Targets::ALL [pub] — crates/lance-graph-codegraph/src/impact_report.rs:52 CONFIRMED +- enum Consumers [pub] — crates/lance-graph-codegraph/src/impact_report.rs:75 CONFIRMED +- struct Scope [pub] — crates/lance-graph-codegraph/src/impact_report.rs:89 CONFIRMED +- enum SymbolState [pub] — crates/lance-graph-codegraph/src/impact_report.rs:108 CONFIRMED +- enum Gap [pub] — crates/lance-graph-codegraph/src/impact_report.rs:123 CONFIRMED +- struct ImpactReport [pub] — crates/lance-graph-codegraph/src/impact_report.rs:234 CONFIRMED +- fn ImpactReport::closed_in_scope [pub] — crates/lance-graph-codegraph/src/impact_report.rs:249 CONFIRMED +- fn Codegraph::impact_report [pub] — crates/lance-graph-codegraph/src/impact_report.rs:429 CONFIRMED - enum Fault [pub(crate)] cfg=test — crates/lance-graph-codegraph/src/lab_wal.rs:19 CONFIRMED - struct LabWal [pub(crate)] cfg=test — crates/lance-graph-codegraph/src/lab_wal.rs:35 CONFIRMED - fn LabWal::new [pub(crate)] cfg=test — crates/lance-graph-codegraph/src/lab_wal.rs:44 CONFIRMED @@ -205,15 +218,13 @@ Generated; the code is the truth. See ../index.md. - mod artifact_bin [pub] — crates/lance-graph-codegraph/src/lib.rs:7 CONFIRMED - mod diag [pub] — crates/lance-graph-codegraph/src/lib.rs:8 CONFIRMED - mod graph [pub] — crates/lance-graph-codegraph/src/lib.rs:9 CONFIRMED -- mod query [pub] — crates/lance-graph-codegraph/src/lib.rs:12 CONFIRMED +- mod impact_report [pub] — crates/lance-graph-codegraph/src/lib.rs:10 CONFIRMED +- mod query [pub] — crates/lance-graph-codegraph/src/lib.rs:13 CONFIRMED - struct Graded [pub] — crates/lance-graph-codegraph/src/query.rs:21 CONFIRMED - struct Calls [pub] — crates/lance-graph-codegraph/src/query.rs:32 CONFIRMED - struct Sites [pub] — crates/lance-graph-codegraph/src/query.rs:41 CONFIRMED - enum QueryRefusal [pub] — crates/lance-graph-codegraph/src/query.rs:52 CONFIRMED -- fn Codegraph::callers [pub] — crates/lance-graph-codegraph/src/query.rs:125 CONFIRMED -- fn Codegraph::callees [pub] — crates/lance-graph-codegraph/src/query.rs:157 CONFIRMED -- fn Codegraph::references [pub] — crates/lance-graph-codegraph/src/query.rs:188 CONFIRMED -- fn Codegraph::impact [pub] — crates/lance-graph-codegraph/src/query.rs:239 CONFIRMED +- truncated (204 total), see symbols.tsv ## Imports from in-scope crates @@ -223,6 +234,7 @@ Generated; the code is the truth. See ../index.md. - use lance_graph_contract::ontology::DisplayTemplate cfg=test — crates/lance-graph-codegraph/src/graph/publication.rs:14 CONFIRMED - use lance_graph_contract::ontology::FieldRef cfg=test — crates/lance-graph-codegraph/src/graph/publication.rs:14 CONFIRMED - use lance_graph_contract::scheduler::DatasetVersion cfg=test — crates/lance-graph-codegraph/src/graph/publication.rs:15 CONFIRMED +- use lance_graph_contract::hash::fnv1a — crates/lance-graph-codegraph/src/impact_report.rs:22 CONFIRMED - use lance_graph_contract::scheduler::DatasetVersion cfg=test — crates/lance-graph-codegraph/src/lab_wal.rs:11 CONFIRMED ### lance-graph-dt64 - use lance_graph_dt64::dr::Axis — crates/lance-graph-codegraph/src/graph.rs:9 CONFIRMED @@ -280,10 +292,12 @@ Generated from Cargo manifests — extracted relationships only. ```mermaid flowchart LR n36["lance-graph-codegraph"] + n39["lance-graph-contract"] n42["lance-graph-dt64"] n47["lance-graph-mask-risc"] n57["lance-graph-quack"] n66["lance-graph-xlog"] + n36 --> n39 n36 --> n42 n36 --> n47 n36 --> n57 diff --git a/.codegraph/crates/lance-graph-contract.index.md b/.codegraph/crates/lance-graph-contract.index.md index b4f802be6..9afc04edf 100644 --- a/.codegraph/crates/lance-graph-contract.index.md +++ b/.codegraph/crates/lance-graph-contract.index.md @@ -38,7 +38,7 @@ Generated; the code is the truth. See ../index.md. - rdep lance-graph-archetype normal — crates/lance-graph-archetype/Cargo.toml:10 CONFIRMED - rdep lance-graph-arm-discovery normal optional — crates/lance-graph-arm-discovery/Cargo.toml:52 CONFIRMED - rdep lance-graph-callcenter normal — crates/lance-graph-callcenter/Cargo.toml:12 CONFIRMED -- rdep lance-graph-codegraph dev — crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED +- rdep lance-graph-codegraph normal — crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED - rdep lance-graph-cognitive normal — crates/lance-graph-cognitive/Cargo.toml:19 CONFIRMED - rdep lance-graph-consumer-conformance normal — crates/lance-graph-consumer-conformance/Cargo.toml:21 CONFIRMED - rdep lance-graph-dir-sim dev — crates/lance-graph-dir-sim/Cargo.toml:52 CONFIRMED @@ -315,6 +315,7 @@ flowchart LR n30["lance-graph-archetype"] n31["lance-graph-arm-discovery"] n33["lance-graph-callcenter"] + n36["lance-graph-codegraph"] n37["lance-graph-cognitive"] n38["lance-graph-consumer-conformance"] n39["lance-graph-contract"] @@ -364,6 +365,7 @@ flowchart LR n30 --> n39 n31 --> n39 n33 --> n39 + n36 --> n39 n37 --> n39 n38 --> n39 n41 --> n39 diff --git a/.codegraph/crates/lance-graph-planner.index.md b/.codegraph/crates/lance-graph-planner.index.md index 59ad1198b..a8d6900b8 100644 --- a/.codegraph/crates/lance-graph-planner.index.md +++ b/.codegraph/crates/lance-graph-planner.index.md @@ -34,7 +34,7 @@ Generated; the code is the truth. See ../index.md. - rdep drf-focus-probe normal optional — crates/drf-focus-probe/Cargo.toml:19 CONFIRMED - rdep lance-graph dev — crates/lance-graph/Cargo.toml:175 CONFIRMED - rdep lance-graph normal optional — crates/lance-graph/Cargo.toml:92 CONFIRMED -- rdep lance-graph-codegraph dev — crates/lance-graph-codegraph/Cargo.toml:22 CONFIRMED +- rdep lance-graph-codegraph dev — crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED - rdep lance-graph-dt64-lance normal — crates/lance-graph-dt64-lance/Cargo.toml:28 CONFIRMED - rdep lance-graph-osint normal — crates/lance-graph-osint/Cargo.toml:11 CONFIRMED - rdep lance-graph-supervisor normal optional — crates/lance-graph-supervisor/Cargo.toml:17 CONFIRMED diff --git a/.codegraph/dependencies.tsv b/.codegraph/dependencies.tsv index 8c57f4551..a5fea7ef9 100644 --- a/.codegraph/dependencies.tsv +++ b/.codegraph/dependencies.tsv @@ -245,13 +245,13 @@ lance-graph-catalog tokio dev false - - crates/lance-graph-catalog/Cargo.toml:28 lance-graph-catalog wiremock dev false - - crates/lance-graph-catalog/Cargo.toml:29 CONFIRMED no lance-graph-codec-research rustfft normal false - - crates/lance-graph-codec-research/Cargo.toml:10 CONFIRMED no lance-graph-codec-research tempfile dev false - - crates/lance-graph-codec-research/Cargo.toml:13 CONFIRMED no -lance-graph-codegraph lance-graph-contract dev false - - crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED yes +lance-graph-codegraph lance-graph-contract normal false - - crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED yes lance-graph-codegraph lance-graph-dt64 normal false - - crates/lance-graph-codegraph/Cargo.toml:13 CONFIRMED yes lance-graph-codegraph lance-graph-mask-risc normal false - - crates/lance-graph-codegraph/Cargo.toml:15 CONFIRMED yes -lance-graph-codegraph lance-graph-planner dev false - - crates/lance-graph-codegraph/Cargo.toml:22 CONFIRMED yes +lance-graph-codegraph lance-graph-planner dev false - - crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED yes lance-graph-codegraph lance-graph-quack normal false - - crates/lance-graph-codegraph/Cargo.toml:14 CONFIRMED yes lance-graph-codegraph lance-graph-xlog normal false - - crates/lance-graph-codegraph/Cargo.toml:12 CONFIRMED yes -lance-graph-codegraph ndarray normal false - - crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED no +lance-graph-codegraph ndarray normal false - - crates/lance-graph-codegraph/Cargo.toml:17 CONFIRMED no lance-graph-codegraph tokio dev false - - crates/lance-graph-codegraph/Cargo.toml:24 CONFIRMED no lance-graph-cognitive holograph normal false - - crates/lance-graph-cognitive/Cargo.toml:18 CONFIRMED yes lance-graph-cognitive lance-graph-contract normal false - - crates/lance-graph-cognitive/Cargo.toml:19 CONFIRMED yes diff --git a/.codegraph/freshness.tsv b/.codegraph/freshness.tsv index c6f966411..7052a8aa9 100644 --- a/.codegraph/freshness.tsv +++ b/.codegraph/freshness.tsv @@ -36,7 +36,7 @@ lance-graph-benches 2f2fb296a08260fb Cargo.toml+src (1 files) lance-graph-callcenter 41a65d38ac70bc87 Cargo.toml+src (36 files) lance-graph-catalog 9d0c34f70c28a465 Cargo.toml+src (10 files) lance-graph-codec-research 8b8afbd9a6d0f359 Cargo.toml+src (11 files) -lance-graph-codegraph bd209bed318ffbbc Cargo.toml+src (9 files) +lance-graph-codegraph 434f8b1c7314d3b0 Cargo.toml+src (10 files) lance-graph-cognitive 93f956ac0d69ca66 Cargo.toml+src (61 files) lance-graph-consumer-conformance 82e980465d770990 Cargo.toml+src (3 files) lance-graph-contract 6b4e13ff1d0afadb Cargo.toml+src (176 files) diff --git a/.codegraph/imports.tsv b/.codegraph/imports.tsv index 448f89dc0..809ca5b47 100644 --- a/.codegraph/imports.tsv +++ b/.codegraph/imports.tsv @@ -4738,17 +4738,17 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 10 lance_graph_x lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 10 lance_graph_xlog::cube::StagedEdges - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 11 lance_graph_xlog::wide::WideRows - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 11 lance_graph_xlog::wide::WideStats - use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1149 super::* - use test POSSIBLE -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1150 std::fs - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1151 std::path::PathBuf - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1152 std::sync::atomic::AtomicU32 - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1152 std::sync::atomic::Ordering - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1347 super::* - use test POSSIBLE -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1348 std::collections::BTreeMap - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1349 std::fs - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1350 std::path::PathBuf - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1351 std::sync::atomic::AtomicU32 - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1351 std::sync::atomic::Ordering - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1186 super::* - use test POSSIBLE +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1187 std::fs - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1188 std::path::PathBuf - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1189 std::sync::atomic::AtomicU32 - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1189 std::sync::atomic::Ordering - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1384 super::* - use test POSSIBLE +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1385 std::collections::BTreeMap - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1386 std::fs - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1387 std::path::PathBuf - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1388 std::sync::atomic::AtomicU32 - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1388 std::sync::atomic::Ordering - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 14 crate::artifact - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 14 crate::artifact::CrateSource - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 14 crate::artifact::Interner - use - CONFIRMED @@ -4807,6 +4807,17 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 22 s lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 23 super::Codegraph - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 23 super::PreparedRefresh - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 24 crate::lab_wal::LabWal - use test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 19 std::collections::BTreeSet - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 20 std::path::Path - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 20 std::path::PathBuf - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 22 lance_graph_contract::hash::fnv1a - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 24 crate::artifact::P_REFERENCES - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 24 crate::artifact::ResidualCounts - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 25 crate::graph::Codegraph - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 25 crate::graph::Pos - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 25 crate::graph::RunStatus - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 26 crate::query::Graded - use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 26 crate::query::QueryRefusal - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 11 lance_graph_contract::scheduler::DatasetVersion - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 13 lance_graph_planner::persist_sink::CommitError - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 13 lance_graph_planner::persist_sink::CommitOutcome - use test CONFIRMED @@ -4820,12 +4831,12 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 14 lance_graph lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 170 super::* - use test POSSIBLE lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 171 lance_graph_planner::persist_sink::CycleFrame - use test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 9 std::collections::BTreeMap - use test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::Codegraph - pub-use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::IntakeRefusal - pub-use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::Pos - pub-use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::RefreshRefusal - pub-use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::RefreshStats - pub-use - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 14 graph::RunStatus - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::Codegraph - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::IntakeRefusal - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::Pos - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::RefreshRefusal - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::RefreshStats - pub-use - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 15 graph::RunStatus - pub-use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/query.rs 10 lance_graph_xlog::cube::CubeRefusal - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/query.rs 10 lance_graph_xlog::cube::RelId - use - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/query.rs 11 lance_graph_xlog::wide::WideRows - use - CONFIRMED diff --git a/.codegraph/index.md b/.codegraph/index.md index d4712c239..eb3f2ddd0 100644 --- a/.codegraph/index.md +++ b/.codegraph/index.md @@ -305,6 +305,7 @@ flowchart LR n33 --> n39 n33 --> n53 n33 --> n59 + n36 --> n39 n36 --> n42 n36 --> n47 n36 --> n57 diff --git a/.codegraph/reverse_dependencies.tsv b/.codegraph/reverse_dependencies.tsv index 0025ed11f..637106e6d 100644 --- a/.codegraph/reverse_dependencies.tsv +++ b/.codegraph/reverse_dependencies.tsv @@ -170,7 +170,7 @@ lance-graph-contract lance-graph normal false - - crates/lance-graph/Cargo.toml: lance-graph-contract lance-graph-archetype normal false - - crates/lance-graph-archetype/Cargo.toml:10 CONFIRMED yes lance-graph-contract lance-graph-arm-discovery normal true - landing crates/lance-graph-arm-discovery/Cargo.toml:52 CONFIRMED yes lance-graph-contract lance-graph-callcenter normal false - - crates/lance-graph-callcenter/Cargo.toml:12 CONFIRMED yes -lance-graph-contract lance-graph-codegraph dev false - - crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED yes +lance-graph-contract lance-graph-codegraph normal false - - crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED yes lance-graph-contract lance-graph-cognitive normal false - - crates/lance-graph-cognitive/Cargo.toml:19 CONFIRMED yes lance-graph-contract lance-graph-consumer-conformance normal false - - crates/lance-graph-consumer-conformance/Cargo.toml:21 CONFIRMED yes lance-graph-contract lance-graph-dir-sim dev false - rbac crates/lance-graph-dir-sim/Cargo.toml:52 CONFIRMED yes @@ -264,7 +264,7 @@ lance-graph-planner drf-focus-probe dev false - handoff crates/drf-focus-probe/C lance-graph-planner drf-focus-probe normal true - handoff crates/drf-focus-probe/Cargo.toml:19 CONFIRMED yes lance-graph-planner lance-graph dev false - planner crates/lance-graph/Cargo.toml:175 CONFIRMED yes lance-graph-planner lance-graph normal true - planner crates/lance-graph/Cargo.toml:92 CONFIRMED yes -lance-graph-planner lance-graph-codegraph dev false - - crates/lance-graph-codegraph/Cargo.toml:22 CONFIRMED yes +lance-graph-planner lance-graph-codegraph dev false - - crates/lance-graph-codegraph/Cargo.toml:23 CONFIRMED yes lance-graph-planner lance-graph-dt64-lance normal false - - crates/lance-graph-dt64-lance/Cargo.toml:28 CONFIRMED yes lance-graph-planner lance-graph-osint normal false - - crates/lance-graph-osint/Cargo.toml:11 CONFIRMED yes lance-graph-planner lance-graph-supervisor normal true - cycle-driver crates/lance-graph-supervisor/Cargo.toml:17 CONFIRMED yes @@ -335,7 +335,7 @@ ndarray jc-batch-probe normal false - - crates/jc-batch-probe/Cargo.toml:21 CONF ndarray lance-graph normal true - ndarray-hpc crates/lance-graph/Cargo.toml:89 CONFIRMED no ndarray lance-graph-arm-discovery normal true - ndarray-simd crates/lance-graph-arm-discovery/Cargo.toml:45 CONFIRMED no ndarray lance-graph-benches normal false - - crates/lance-graph-benches/Cargo.toml:30 CONFIRMED no -ndarray lance-graph-codegraph normal false - - crates/lance-graph-codegraph/Cargo.toml:16 CONFIRMED no +ndarray lance-graph-codegraph normal false - - crates/lance-graph-codegraph/Cargo.toml:17 CONFIRMED no ndarray lance-graph-cognitive normal false - - crates/lance-graph-cognitive/Cargo.toml:17 CONFIRMED no ndarray lance-graph-mask-risc normal false - - crates/lance-graph-mask-risc/Cargo.toml:20 CONFIRMED no ndarray lance-graph-onnx normal false - - crates/lance-graph-onnx/Cargo.toml:12 CONFIRMED no diff --git a/.codegraph/symbols.tsv b/.codegraph/symbols.tsv index bea7437c0..bd242fe13 100644 --- a/.codegraph/symbols.tsv +++ b/.codegraph/symbols.tsv @@ -8687,26 +8687,29 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1001 fn Codegrap lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1009 fn Codegraph::any_partial pub(crate) - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1014 fn Codegraph::words pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1019 fn Codegraph::mask_of pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1030 fn Codegraph::crate_status pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1036 fn Codegraph::rows pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1041 fn Codegraph::row_predicate pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1046 fn Codegraph::row_subject pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1051 fn Codegraph::row_object pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1056 fn Codegraph::site pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1070 fn Codegraph::cube pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1075 fn Codegraph::cube_mut pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1081 fn Codegraph::cube_and_table_mut pub(crate) - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1086 fn Codegraph::table pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1091 fn Codegraph::fns pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1095 fn Codegraph::public pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1099 fn Codegraph::impl_subjects pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1103 fn Codegraph::synthetic pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1107 fn Codegraph::referenced pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1111 fn Codegraph::called pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1115 fn Codegraph::may_targets pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1119 fn Codegraph::dynamic_callers pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1123 fn Codegraph::stored_owner pub - CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1129 fn Codegraph::closure pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1031 fn Codegraph::crate_list pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1052 fn Codegraph::has_rows pub(crate) - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1062 fn Codegraph::crate_residuals pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1067 fn Codegraph::crate_status pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1073 fn Codegraph::rows pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1078 fn Codegraph::row_predicate pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1083 fn Codegraph::row_subject pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1088 fn Codegraph::row_object pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1093 fn Codegraph::site pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1107 fn Codegraph::cube pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1112 fn Codegraph::cube_mut pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1118 fn Codegraph::cube_and_table_mut pub(crate) - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1123 fn Codegraph::table pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1128 fn Codegraph::fns pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1132 fn Codegraph::public pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1136 fn Codegraph::impl_subjects pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1140 fn Codegraph::synthetic pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1144 fn Codegraph::referenced pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1148 fn Codegraph::called pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1152 fn Codegraph::may_targets pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1156 fn Codegraph::dynamic_callers pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1160 fn Codegraph::stored_owner pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 1166 fn Codegraph::closure pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 130 impl PosAt for Positions - - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 158 impl PosAt for PosStage<'_> - - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 167 impl PosStage<'a> - - CONFIRMED @@ -8738,6 +8741,19 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/graph.rs 996 fn Codegraph lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 108 impl Record - test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 165 impl W0 - test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/graph/publication.rs 65 impl ClassView for PubView - test CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 108 enum SymbolState pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 123 enum Gap pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 234 struct ImpactReport pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 245 impl ImpactReport - - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 249 fn ImpactReport::closed_in_scope pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 31 struct Targets pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 42 impl Targets - - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 421 impl Codegraph - - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 429 fn Codegraph::impact_report pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 44 const Targets::LIB_ONLY pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 52 const Targets::ALL pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 75 enum Consumers pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/impact_report.rs 89 struct Scope pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 19 enum Fault pub(crate) test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 35 struct LabWal pub(crate) test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 43 impl LabWal - test CONFIRMED @@ -8747,7 +8763,8 @@ lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 58 fn LabWal:: lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 64 fn LabWal::reverse_physical_order pub(crate) test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 70 fn LabWal::image pub(crate) test CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lab_wal.rs 78 impl WalSink for LabWal - test CONFIRMED -lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 12 mod query pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 10 mod impact_report pub - CONFIRMED +lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 13 mod query pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 6 mod artifact pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 7 mod artifact_bin pub - CONFIRMED lance-graph-codegraph crates/lance-graph-codegraph/src/lib.rs 8 mod diag pub - CONFIRMED diff --git a/.codegraph/views/BindStore.md b/.codegraph/views/BindStore.md index 0af6749c8..724fbe3c5 100644 --- a/.codegraph/views/BindStore.md +++ b/.codegraph/views/BindStore.md @@ -53,7 +53,7 @@ Generated; the code is the truth. Same-name symbols stay separate per file. - crates/grammar-constraints-probe/src/when.rs:39 POSSIBLE - crates/grammar-constraints-probe/src/when.rs:313 POSSIBLE - crates/grammar-constraints-probe/src/when.rs:414 POSSIBLE -- crates/lance-graph-codegraph/src/graph.rs:1564 POSSIBLE +- crates/lance-graph-codegraph/src/graph.rs:1601 POSSIBLE - crates/lance-graph-codegraph/src/graph/publication.rs:6 POSSIBLE - crates/lance-graph-codegraph/src/graph/publication.rs:16 POSSIBLE - crates/lance-graph-codegraph/src/graph/publication.rs:39 POSSIBLE diff --git a/crates/lance-graph-codegraph/Cargo.toml b/crates/lance-graph-codegraph/Cargo.toml index f70dc454e..7b3c74e30 100644 --- a/crates/lance-graph-codegraph/Cargo.toml +++ b/crates/lance-graph-codegraph/Cargo.toml @@ -13,6 +13,7 @@ lance-graph-xlog = { path = "../lance-graph-xlog" } lance-graph-dt64 = { path = "../lance-graph-dt64" } lance-graph-quack = { path = "../lance-graph-quack" } lance-graph-mask-risc = { path = "../lance-graph-mask-risc" } +lance-graph-contract = { path = "../lance-graph-contract" } ndarray = { git = "https://github.com/AdaWorldAPI/ndarray", branch = "master", default-features = false, features = ["std"] } [dev-dependencies] @@ -20,7 +21,6 @@ ndarray = { git = "https://github.com/AdaWorldAPI/ndarray", branch = "master", d # `CommitError`, `DetachedCycleBatch`) for the G10 x G11 publication lab. Never # a production edge from the codegraph to the planner. lance-graph-planner = { path = "../lance-graph-planner" } -lance-graph-contract = { path = "../lance-graph-contract" } tokio = { version = "1", features = ["rt"] } # One ndarray (CARGO-COMPUTE-SUBSTRATE rule 9): the planner names the sibling diff --git a/crates/lance-graph-codegraph/examples/cg_query.rs b/crates/lance-graph-codegraph/examples/cg_query.rs index 4b4975ef8..1011aeaac 100644 --- a/crates/lance-graph-codegraph/examples/cg_query.rs +++ b/crates/lance-graph-codegraph/examples/cg_query.rs @@ -4,8 +4,18 @@ //! ```text //! cargo run --release --example cg_query -- symbols [SUBSTRING] //! cargo run --release --example cg_query -- callers|callees|impact|refs SYMBOL +//! cargo run --release --example cg_query -- impact-report SYMBOL \ +//! [--deps a,b | --consumers-closed] [--targets CRATE=lib,tests,examples,benches]... \ +//! [--exclude CRATE=REGION]... [--no-freshness] //! ``` //! +//! `impact-report` (alias `report`) prints [`Codegraph::impact_report`]: whether the symbol has a +//! position, the impact masks, the stored crates, and every coverage gap of the declared scope, +//! one `GAP` line each. Nothing is assumed: a crate without `--targets` is reported as undeclared +//! (`--targets '*=lib,tests'` declares every stored crate), consumers are undeclared without +//! `--deps` (`--deps ""` declares none) or `--consumers-closed`, and `--exclude` names code the +//! extraction did not translate (an opaque module, a feature left off). +//! //! Output is tab-separated, one fact per line. Every result line carries its //! grade (`CONFIRMED`, `POSSIBLE`, `UNKNOWN`); a refusal is printed verbatim //! with `REFUSED` and exit code 3, never as an empty answer. Callers and impact of @@ -17,6 +27,7 @@ use std::path::{Path, PathBuf}; use std::process::ExitCode; +use lance_graph_codegraph::impact_report::{Consumers, Scope, SymbolState, Targets}; use lance_graph_codegraph::Codegraph; fn line_of(src_root: &Path, file: &str, offset: u32) -> String { @@ -61,10 +72,136 @@ fn observed_files(g: &Codegraph) -> Vec { files } +fn parse_targets(list: &str) -> Option { + let mut t = Targets::default(); + for k in list.split(',') { + match k { + "lib" => t.lib = true, + "tests" => t.tests = true, + "examples" => t.examples = true, + "benches" => t.benches = true, + _ => return None, + } + } + Some(t) +} + +/// `impact-report SYMBOL [flags]`. The flags declare what the rows cannot say. +fn impact_report(g: &Codegraph, store: &Path, src: &Path, sym: &str, flags: &[String]) -> ExitCode { + let mut scope = Scope { + store: Some(store.to_path_buf()), + source_root: Some(src.to_path_buf()), + ..Scope::default() + }; + let pair = |v: Option<&String>| { + v.and_then(|v| v.split_once('=')) + .map(|(a, b)| (a.to_string(), b.to_string())) + }; + let mut it = flags.iter(); + while let Some(f) = it.next() { + match f.as_str() { + "--no-freshness" => { + scope.store = None; + scope.source_root = None; + } + "--consumers-closed" | "--deps" if scope.consumers != Consumers::NotDeclared => { + eprintln!("--deps and --consumers-closed are given once and exclude each other"); + return ExitCode::from(2); + } + "--consumers-closed" => scope.consumers = Consumers::AllInStore, + "--deps" => match it.next() { + Some(v) if !v.starts_with("--") => { + scope.consumers = Consumers::Declared( + v.split(',') + .filter(|s| !s.is_empty()) + .map(str::to_string) + .collect(), + ); + } + _ => { + eprintln!("--deps needs a value: a comma-separated list, or \"\" for none"); + return ExitCode::from(2); + } + }, + "--targets" => match pair(it.next()) { + Some((k, list)) => match parse_targets(&list) { + Some(t) if k == "*" => { + scope + .targets + .extend(g.crate_list().into_iter().map(|(c, _)| (c, t))); + } + Some(t) => scope.targets.push((k, t)), + None => { + eprintln!("bad --targets list {list}"); + return ExitCode::from(2); + } + }, + None => { + eprintln!("--targets needs CRATE=LIST"); + return ExitCode::from(2); + } + }, + "--exclude" => match pair(it.next()) { + Some((k, region)) => match scope.excluded.iter_mut().find(|(n, _)| *n == k) { + Some((_, r)) => r.push(region), + None => scope.excluded.push((k, vec![region])), + }, + None => { + eprintln!("--exclude needs CRATE=REGION"); + return ExitCode::from(2); + } + }, + other => { + eprintln!("unknown flag {other}"); + return ExitCode::from(2); + } + } + } + let r = match g.impact_report(sym, &scope) { + Ok(r) => r, + Err(e) => { + println!("REFUSED\timpact-report\t{e:?}"); + return ExitCode::from(3); + } + }; + let found = match r.symbol { + SymbolState::Found(p) => { + println!("symbol\tFOUND\t{p}\t{sym}"); + true + } + SymbolState::Missing { owner_stored } => { + println!("symbol\tMISSING\towner_stored={owner_stored}\t{sym}"); + false + } + }; + for (grade, mask) in [ + ("CONFIRMED", &r.graded.confirmed), + ("POSSIBLE", &r.graded.possible), + ("UNKNOWN", &r.graded.unknown), + ] { + for n in names(g, mask) { + println!("impact\t{grade}\t{n}"); + } + } + for (c, st) in &r.observed { + println!("observed\t{c}\t{st:?}"); + } + for gap in &r.gaps { + println!("GAP\t{gap:?}"); + } + println!("closed_in_scope\t{}", r.closed_in_scope()); + // A missing symbol is not an empty impact: exit 4, as the other queries do. + if found { + ExitCode::SUCCESS + } else { + ExitCode::from(4) + } +} + fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); if args.len() < 3 { - eprintln!("usage: cg_query symbols [SUBSTR] | callers|callees|impact|refs SYMBOL"); + eprintln!("usage: cg_query symbols [SUBSTR] | sweep | callers|callees|impact|refs SYMBOL | impact-report SYMBOL [--deps a,b | --consumers-closed] [--targets CRATE=LIST]... [--exclude CRATE=REGION]... [--no-freshness]"); return ExitCode::from(2); } let (store, src, cmd) = ( @@ -135,6 +272,13 @@ fn main() -> ExitCode { } return ExitCode::SUCCESS; } + if cmd == "impact-report" || cmd == "report" { + let Some(sym) = args.get(3) else { + eprintln!("{cmd} needs a SYMBOL"); + return ExitCode::from(2); + }; + return impact_report(&g, &store, &src, sym, &args[4..]); + } let Some(sym) = args.get(3) else { eprintln!("{cmd} needs a SYMBOL"); return ExitCode::from(2); diff --git a/crates/lance-graph-codegraph/src/graph.rs b/crates/lance-graph-codegraph/src/graph.rs index d6a20e156..ba81f46f4 100644 --- a/crates/lance-graph-codegraph/src/graph.rs +++ b/crates/lance-graph-codegraph/src/graph.rs @@ -1027,6 +1027,43 @@ impl Codegraph { } /// The extractor status of `krate`, from its CRATE row. + /// Every stored crate with its extractor status, sorted by name. + pub fn crate_list(&self) -> Vec<(String, RunStatus)> { + // Every crate that was read, not only those with a CRATE row: a rows file without one + // says nothing about its own completeness, so it is `Partial`. + let mut ids: Vec = self.crates.keys().copied().collect(); + ids.extend(self.lanes.crates()); + ids.sort_unstable(); + ids.dedup(); + let mut out: Vec<(String, RunStatus)> = ids + .into_iter() + .map(|k| { + let status = self.crates.get(&k).copied().unwrap_or(RunStatus::Partial); + (self.symbols.name(k).to_string(), status) + }) + .collect(); + out.sort_by(|a, b| a.0.cmp(&b.0)); + out + } + + /// Whether any row currently names `symbol` as subject, object or dispatch target. A + /// position outlives the rows that created it (a refresh never renumbers), so a position + /// alone does not show the symbol is still in the artifacts. + pub(crate) fn has_rows(&self, symbol: &str) -> bool { + let Some(id) = self.symbols.get(symbol) else { + return false; + }; + let l = &self.lanes; + l.subject.contains(&id) || l.object.contains(&id) || l.may_target.contains(&id) + } + + /// The residuals read beside `krate`'s rows: `None` when the crate is not in the store, + /// `Some(None)` when it was read without a residuals file. + pub fn crate_residuals(&self, krate: &str) -> Option> { + let id = self.symbols.get(krate)?; + self.lanes.residuals_of(id) + } + pub fn crate_status(&self, krate: &str) -> Option { let id = self.symbols.get(krate)?; self.crates.get(&id).copied() diff --git a/crates/lance-graph-codegraph/src/impact_report.rs b/crates/lance-graph-codegraph/src/impact_report.rs new file mode 100644 index 000000000..96ca8cf29 --- /dev/null +++ b/crates/lance-graph-codegraph/src/impact_report.rs @@ -0,0 +1,611 @@ +//! A coverage-aware impact report: [`Codegraph::impact`] plus a typed account of what the +//! answer could not see. +//! +//! [`Codegraph::impact`] answers in symbol positions, and that stays as it is. A caller the +//! store never observed has no position, so an empty answer cannot tell "nothing depends on +//! this" from "the dependants were never extracted". This report keeps the impact masks exactly +//! as `impact` returns them and adds a list of [`Gap`]s. Missing coverage is never written into +//! [`Graded::unknown`], which stays the set of observed functions a dynamic call may reach. +//! +//! Some coverage facts cannot be read off the rows: which targets were extracted, which crates +//! depend on the symbol's crate, which regions the extraction skipped, and whether a source file +//! changed since extraction. The caller declares those in a [`Scope`]. A fact it does not +//! declare becomes a gap, never an assumption; a fact it does declare is trusted. +//! +//! [`ImpactReport::closed_in_scope`] is the only "nothing else" claim this module makes, and it +//! is local: no gap is known *within the declared scope*. Like +//! [`crate::diag::Coverage::no_known_gap`], it is never a proof that the harvest saw everything. + +use std::collections::BTreeSet; +use std::path::{Path, PathBuf}; + +use lance_graph_contract::hash::fnv1a; + +use crate::artifact::{ResidualCounts, P_REFERENCES}; +use crate::graph::{Codegraph, Pos, RunStatus}; +use crate::query::{Graded, QueryRefusal}; + +/// Which targets of one crate were extracted into the store. The store does not record this; +/// the caller declares it from how the extractor was run. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Targets { + /// The library target. + pub lib: bool, + /// Integration tests and `#[cfg(test)]` unit tests. + pub tests: bool, + /// Examples. + pub examples: bool, + /// Benches. + pub benches: bool, +} + +impl Targets { + /// The library target only (Charon's default `charon cargo` run). + pub const LIB_ONLY: Targets = Targets { + lib: true, + tests: false, + examples: false, + benches: false, + }; + + /// Every target kind. + pub const ALL: Targets = Targets { + lib: true, + tests: true, + examples: true, + benches: true, + }; + + fn missing(self) -> Targets { + Targets { + lib: !self.lib, + tests: !self.tests, + examples: !self.examples, + benches: !self.benches, + } + } + + fn any(self) -> bool { + self.lib || self.tests || self.examples || self.benches + } +} + +/// The crates that can call into the queried symbol's crate, as the caller knows them. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum Consumers { + /// Not declared: callers outside the store are unknown. + #[default] + NotDeclared, + /// Exactly these crates depend on the symbol's crate (an empty list means none do). One + /// that is not in the store is reported by name. + Declared(Vec), + /// Every dependent crate is in the store, without naming them (for example an unpublished + /// crate whose whole dependent set was extracted). Every stored crate is then checked. + AllInStore, +} + +/// What the caller asserts about the analysis beyond the store's own rows. +#[derive(Debug, Clone, Default)] +pub struct Scope { + /// The store directory, for `.files.tsv`. + pub store: Option, + /// The source root the recorded paths are relative to. Without both `store` and + /// `source_root`, freshness is not checked, which is itself a gap. + pub source_root: Option, + /// Extracted targets per stored crate name. A crate the answer depends on without an entry + /// is [`Gap::TargetsUndeclared`]. + pub targets: Vec<(String, Targets)>, + /// The crates that can call into the symbol's crate. + pub consumers: Consumers, + /// Code of a stored crate the extraction did not translate, per crate name: modules passed + /// to Charon as opaque, cargo features left off, cfg branches not taken. The store cannot + /// see what is inside, so a caller, callee or definition there has no position. + pub excluded: Vec<(String, Vec)>, +} + +/// Whether the queried symbol has a position with current rows. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SymbolState { + /// The symbol is in the store. + Found(Pos), + /// No current row names the symbol. Not "no impact". + Missing { + /// The symbol's first path segment names a stored crate: the crate was read and does + /// not define or mention it. When false, the symbol lies outside every stored crate + /// and nothing was looked at. + owner_stored: bool, + }, +} + +/// One reason the impact answer may be incomplete. Every variant comes from a store fact or a +/// declared scope fact; none is inferred. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Gap { + /// The symbol, or something that reaches it, is `PUBLIC` and the consumers were not + /// declared: crates outside the store can call it. + ExternalConsumersUnobserved { + /// The symbol's crate. + owner: String, + /// Other stored crates with a confirmed caller in the answer, sorted. + observed_consumers: Vec, + }, + /// Declared consumers whose crates are not in the store; their callers are unobserved. + ConsumersNotInStore { + /// Crate names, sorted. + crates: Vec, + }, + /// Targets of a crate the answer depends on that were not extracted, so callers there (for + /// example test functions) have no positions. + TargetsNotExtracted { + /// The crate. + krate: String, + /// The target kinds that were not extracted. + missing: Targets, + }, + /// No target declaration for a stored crate the answer depends on. + TargetsUndeclared { + /// The crate. + krate: String, + }, + /// The declared scope excludes regions of the crate from extraction. Calls inside them have + /// no rows, and a symbol defined only there is missing without being absent. + RegionsExcluded { + /// The crate. + krate: String, + /// The excluded regions, as declared. + regions: Vec, + }, + /// Callers reach the symbol through trait dispatch (`graded.possible`), which the compiler + /// did not resolve to this implementation. + TraitDispatch { + /// Positions in `graded.possible`. + possible: u32, + }, + /// A dynamic call or function pointer may reach the symbol or something it reaches; its + /// target was not named by the compiler. Also reported with an empty `graded.unknown` when + /// the symbol is an impl or is referenced and the store holds a dynamic call: the caller of + /// that call may already be in the answer for another reason, which does not resolve where + /// the dynamic call goes. + DynamicCalls { + /// Positions in `graded.unknown`. + unknown: u32, + /// `UNRESOLVED_CALL` rows in the whole store. + store_dynamic_calls: u32, + }, + /// Functions that take the address of the symbol, or of a function in the answer, without + /// calling it (`REFERENCES` rows) and are in none of the three masks. A reference is not a + /// call edge, so they are not graded; whether they pass the address on to a caller is not + /// known. A store fact: it needs no dynamic call anywhere in the store. + ReferrersOutsideAnswer { + /// Distinct referrers outside the answer. + referrers: u32, + }, + /// A crate's harvest recorded residuals, did not finish, or has no residuals file. + HarvestIncomplete { + /// The crate. + krate: String, + /// Residual counts (zero when only the run status or the missing file is the cause). + residuals: ResidualCounts, + /// The CRATE row is absent or not `COMPLETE`. + partial: bool, + /// No `.residuals.tsv` was read. + no_residuals_file: bool, + }, + /// Recorded source files whose content no longer matches the store. + StaleSource { + /// The crate. + krate: String, + /// Paths relative to the source root, sorted. + files: Vec, + }, + /// Recorded source files that are absent from the source root. + MissingSource { + /// The crate. + krate: String, + /// Paths relative to the source root, sorted. + files: Vec, + }, + /// Rust files under a directory the extraction read from that it did not read itself: + /// modules behind a disabled feature or cfg, other targets' roots, files never declared as + /// modules. Their code has no rows. + SourceFilesUnread { + /// The crate. + krate: String, + /// The unread `.rs` files, relative to the source root, sorted. + files: Vec, + }, + /// The crate has no `.files.tsv`, so freshness cannot be checked. + FilesUnrecorded { + /// The crate. + krate: String, + }, + /// The crate's `.files.tsv` has no `#schema` line or a row without `path` or `fnv64`, + /// so its sources could not be compared. + InvalidFileList { + /// The crate. + krate: String, + }, + /// No store directory or source root was given; freshness was not checked. + FreshnessUnchecked, +} + +/// [`Codegraph::impact`] with its coverage. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImpactReport { + /// Exactly what [`Codegraph::impact`] returns (empty masks when the symbol is missing). + pub graded: Graded, + /// Whether the symbol has a position with current rows. + pub symbol: SymbolState, + /// Every crate in the store with its extractor status, sorted by name. + pub observed: Vec<(String, RunStatus)>, + /// Every known reason the answer may be incomplete. + pub gaps: Vec, +} + +impl ImpactReport { + /// The symbol is in the store and no gap is known within the declared scope. This is a + /// local statement: it never claims anything about crates, targets or revisions outside + /// the scope, and it is not a proof of completeness. + pub fn closed_in_scope(&self) -> bool { + matches!(self.symbol, SymbolState::Found(_)) && self.gaps.is_empty() + } +} + +fn owner_of(symbol: &str) -> &str { + symbol.split("::").next().unwrap_or(symbol) +} + +fn has(mask: &[u64], p: Pos) -> bool { + mask.get(p as usize / 64) + .is_some_and(|w| w >> (p % 64) & 1 == 1) +} + +fn bits(mask: &[u64]) -> impl Iterator + '_ { + (0..mask.len() * 64) + .filter(|&p| mask[p / 64] >> (p % 64) & 1 == 1) + .filter_map(|p| Pos::try_from(p).ok()) +} + +fn popcount(mask: &[u64]) -> u32 { + mask.iter().map(|w| w.count_ones()).sum() +} + +/// What one crate's file list says about its sources. +#[derive(Default)] +struct FileGaps { + stale: Vec, + missing: Vec, + unread: Vec, + invalid: bool, +} + +/// The file gaps of `krate`, or `None` when the crate has no files table. +/// +/// Stale: a recorded file whose digest differs under `root`. Missing: a recorded file that is +/// not there. Unread: a `.rs` file below the top directory of a recorded path that the table +/// does not list; a recorded file at the root names no directory, so nothing is scanned for it. +fn file_gaps(store: &Path, root: &Path, krate: &str) -> Option { + let table = std::fs::read_to_string(store.join(format!("{krate}.files.tsv"))).ok()?; + let mut out = FileGaps::default(); + let mut cols: Option<(usize, usize)> = None; + let mut recorded = BTreeSet::new(); + let mut tops = BTreeSet::new(); + for line in table.lines() { + if let Some(h) = line.strip_prefix("#schema ") { + let names: Vec<&str> = h.split('\t').collect(); + cols = names + .iter() + .position(|c| *c == "path") + .zip(names.iter().position(|c| *c == "fnv64")); + continue; + } + if line.is_empty() || line.starts_with('#') { + continue; + } + let Some((path_col, hash_col)) = cols else { + out.invalid = true; + continue; + }; + let fields: Vec<&str> = line.split('\t').collect(); + let (Some(path), Some(want)) = (fields.get(path_col), fields.get(hash_col)) else { + out.invalid = true; + continue; + }; + recorded.insert((*path).to_owned()); + if let Some((top, _)) = path.split_once('/') { + if top != "." && top != ".." { + tops.insert(top.to_owned()); + } + } + match std::fs::read(root.join(path)) { + Err(_) => out.missing.push((*path).to_owned()), + Ok(bytes) if format!("{:016x}", fnv1a(&bytes)) != *want => { + out.stale.push((*path).to_owned()); + } + Ok(_) => {} + } + } + if cols.is_none() { + out.invalid = true; + } + for top in &tops { + rust_files(root, top, &mut out.unread); + } + out.unread.retain(|f| !recorded.contains(f)); + out.stale.sort(); + out.missing.sort(); + out.unread.sort(); + Some(out) +} + +/// Every `.rs` file below `root/rel`, as paths relative to `root`. +fn rust_files(root: &Path, rel: &str, out: &mut Vec) { + let Ok(entries) = std::fs::read_dir(root.join(rel)) else { + return; + }; + for e in entries.flatten() { + let name = e.file_name().to_string_lossy().into_owned(); + let path = format!("{rel}/{name}"); + match e.file_type() { + Ok(t) if t.is_dir() => rust_files(root, &path, out), + Ok(t) if t.is_file() && name.ends_with(".rs") => out.push(path), + _ => {} + } + } +} + +/// Freshness and unread-file gaps of `krates` under the declared store and source root; +/// [`Gap::FreshnessUnchecked`] when either is missing. +fn freshness_gaps<'a>( + krates: impl IntoIterator, + scope: &Scope, + gaps: &mut Vec, +) { + let (Some(store), Some(root)) = (&scope.store, &scope.source_root) else { + gaps.push(Gap::FreshnessUnchecked); + return; + }; + for k in krates { + let Some(f) = file_gaps(store, root, k) else { + gaps.push(Gap::FilesUnrecorded { krate: k.clone() }); + continue; + }; + if f.invalid { + gaps.push(Gap::InvalidFileList { krate: k.clone() }); + } + if !f.stale.is_empty() { + gaps.push(Gap::StaleSource { + krate: k.clone(), + files: f.stale, + }); + } + if !f.missing.is_empty() { + gaps.push(Gap::MissingSource { + krate: k.clone(), + files: f.missing, + }); + } + if !f.unread.is_empty() { + gaps.push(Gap::SourceFilesUnread { + krate: k.clone(), + files: f.unread, + }); + } + } +} + +/// The declared-scope gaps of one stored crate: its targets and its excluded regions. +fn scope_gaps(k: &str, scope: &Scope, gaps: &mut Vec) { + match scope.targets.iter().find(|(n, _)| n == k) { + None => gaps.push(Gap::TargetsUndeclared { + krate: k.to_owned(), + }), + Some((_, t)) => { + let missing = t.missing(); + if missing.any() { + gaps.push(Gap::TargetsNotExtracted { + krate: k.to_owned(), + missing, + }); + } + } + } + if let Some((_, regions)) = scope.excluded.iter().find(|(n, r)| n == k && !r.is_empty()) { + gaps.push(Gap::RegionsExcluded { + krate: k.to_owned(), + regions: regions.clone(), + }); + } +} + +impl Codegraph { + /// [`Codegraph::impact`] of `symbol`, with the gaps of `scope`. + /// + /// Gaps are reported for the crates the answer depends on: the symbol's own crate, every + /// stored crate holding a function in the answer, and the stored crates that can be + /// consumers. Those are the declared ones, or every stored crate when the consumers are not + /// named and the symbol or one of its callers is public. The masks are those of `impact`; this method never adds a position for an + /// unobserved caller. + pub fn impact_report(&self, symbol: &str, scope: &Scope) -> Result { + let observed = self.crate_list(); + let stored: BTreeSet<&str> = observed.iter().map(|(c, _)| c.as_str()).collect(); + let owner = owner_of(symbol).to_owned(); + let owner_stored = stored.contains(owner.as_str()); + let mut gaps = Vec::new(); + + // A position alone is not enough: it survives a refresh that removed every row. + let Some(g) = self.pos(symbol).filter(|_| self.has_rows(symbol)) else { + // A missing symbol in a stored crate is absent only from what was extracted: + // undeclared or missing targets, excluded regions, an incomplete harvest and unread + // files are each a place it may still be defined. + if owner_stored { + scope_gaps(&owner, scope, &mut gaps); + self.harvest_gaps(&owner, &observed, &mut gaps); + freshness_gaps([&owner], scope, &mut gaps); + } + let z = vec![0u64; self.words()]; + return Ok(ImpactReport { + graded: Graded { + confirmed: z.clone(), + possible: z.clone(), + unknown: z, + }, + symbol: SymbolState::Missing { owner_stored }, + observed, + gaps, + }); + }; + let graded = self.impact(g)?; + + // Crates the answer depends on: the owner, plus every stored crate holding a graded + // function. + let mut involved: BTreeSet = BTreeSet::new(); + if owner_stored { + involved.insert(owner.clone()); + } + for m in [&graded.confirmed, &graded.possible, &graded.unknown] { + for p in bits(m) { + if let Some(k) = self.symbol(p).map(owner_of) { + if stored.contains(k) { + involved.insert(k.to_owned()); + } + } + } + } + let observed_consumers: BTreeSet = bits(&graded.confirmed) + .filter_map(|p| self.symbol(p).map(owner_of)) + .filter(|k| *k != owner && stored.contains(k)) + .map(str::to_owned) + .collect(); + + // The consumer frontier: the symbol, or anything that reaches it, is public. External + // callers of a public caller are affected too. + let frontier_public = has(self.public(), g) + || [&graded.confirmed, &graded.possible, &graded.unknown] + .iter() + .any(|m| m.iter().zip(self.public()).any(|(a, b)| a & b != 0)); + match &scope.consumers { + Consumers::NotDeclared => { + if frontier_public { + // Any stored crate may be a consumer too, so each one's coverage counts. + involved.extend(stored.iter().map(|k| (*k).to_owned())); + gaps.push(Gap::ExternalConsumersUnobserved { + owner: owner.clone(), + observed_consumers: observed_consumers.into_iter().collect(), + }); + } + } + Consumers::Declared(deps) => { + // A declared consumer may call from code it did not extract and so have no + // position here; its own coverage decides the answer. + let mut absent = BTreeSet::new(); + for d in deps { + if stored.contains(d.as_str()) { + involved.insert(d.clone()); + } else { + absent.insert(d.clone()); + } + } + if !absent.is_empty() { + gaps.push(Gap::ConsumersNotInStore { + crates: absent.into_iter().collect(), + }); + } + } + Consumers::AllInStore => { + if frontier_public { + involved.extend(stored.iter().map(|k| (*k).to_owned())); + } + } + } + + for k in &involved { + scope_gaps(k, scope, &mut gaps); + } + + let possible = popcount(&graded.possible); + if possible > 0 { + gaps.push(Gap::TraitDispatch { possible }); + } + let unknown = popcount(&graded.unknown); + let store_dynamic_calls = self.coverage().dynamic_calls; + if unknown > 0 || (store_dynamic_calls > 0 && self.dynamic_may_reach(g, &graded)) { + gaps.push(Gap::DynamicCalls { + unknown, + store_dynamic_calls, + }); + } + + let referrers = self.referrers_outside(g, &graded); + if referrers > 0 { + gaps.push(Gap::ReferrersOutsideAnswer { referrers }); + } + + for k in &involved { + self.harvest_gaps(k, &observed, &mut gaps); + } + freshness_gaps(&involved, scope, &mut gaps); + + Ok(ImpactReport { + graded, + symbol: SymbolState::Found(g), + observed, + gaps, + }) + } + + /// [`Gap::HarvestIncomplete`] for `k` when its harvest recorded residuals, did not finish, + /// or has no residuals file. + fn harvest_gaps(&self, k: &str, observed: &[(String, RunStatus)], gaps: &mut Vec) { + let (residuals, no_residuals_file) = match self.crate_residuals(k) { + Some(Some(r)) => (r, false), + _ => (ResidualCounts::default(), true), + }; + let partial = observed + .iter() + .any(|(c, s)| c == k && *s == RunStatus::Partial); + if residuals.total() > 0 || no_residuals_file || partial { + gaps.push(Gap::HarvestIncomplete { + krate: k.to_owned(), + residuals, + partial, + no_residuals_file, + }); + } + } + + /// Distinct subjects of `REFERENCES` rows whose object is `g` or a graded position, and that + /// are themselves neither `g` nor graded. + fn referrers_outside(&self, g: Pos, graded: &Graded) -> u32 { + let in_answer = |p: Pos| { + p == g + || has(&graded.confirmed, p) + || has(&graded.possible, p) + || has(&graded.unknown, p) + }; + let l = self.lanes(); + let mut outside = BTreeSet::new(); + for i in 0..l.len() { + if l.pred[i] != P_REFERENCES { + continue; + } + let object_in = self.pos_of_id(l.object[i]).is_some_and(in_answer); + let subject_in = self.pos_of_id(l.subject[i]).is_some_and(in_answer); + if object_in && !subject_in { + outside.insert(l.subject[i]); + } + } + outside.len() as u32 + } + + /// Whether a dynamic call may reach `g`: `g`, or a function in the answer + /// (each of which reaches `g`), is an impl or referenced. Same condition + /// as `Codegraph::impact` uses before it fills `unknown`. + fn dynamic_may_reach(&self, g: Pos, graded: &Graded) -> bool { + let target = |p: Pos| has(self.impl_subjects(), p) || has(self.referenced(), p); + target(g) + || (0..self.words() as u32 * 64) + .filter(|p| has(&graded.confirmed, *p) || has(&graded.possible, *p)) + .any(target) + } +} diff --git a/crates/lance-graph-codegraph/src/lib.rs b/crates/lance-graph-codegraph/src/lib.rs index 4a7144256..f81ba6eb2 100644 --- a/crates/lance-graph-codegraph/src/lib.rs +++ b/crates/lance-graph-codegraph/src/lib.rs @@ -7,6 +7,7 @@ pub mod artifact; pub mod artifact_bin; pub mod diag; pub mod graph; +pub mod impact_report; #[cfg(test)] mod lab_wal; pub mod query; diff --git a/crates/lance-graph-codegraph/tests/impact_coverage.rs b/crates/lance-graph-codegraph/tests/impact_coverage.rs new file mode 100644 index 000000000..9fe2d9a19 --- /dev/null +++ b/crates/lance-graph-codegraph/tests/impact_coverage.rs @@ -0,0 +1,1518 @@ +//! Coverage-aware impact (D-CODEGRAPH-RECON-2, Gate A), over the merged `impact_report` API. +//! +//! `suite_a` runs real Charon artifacts (`tests/data/rev1`: `cg_lib` and its consumer `cg_app`, +//! extracted by ruff_charon from `crates/sag-ra-probe/fixtures/codegraph/rev1`). `suite_b` runs +//! synthetic stores with exact rows, each gate with a can-fire test and a can-stay-silent twin. +//! `narrowing` pins that each declared fact removes the gap it answers. + +mod suite_a { + use std::path::{Path, PathBuf}; + + use lance_graph_codegraph::impact_report::{ + Consumers, Gap, ImpactReport, Scope, SymbolState, Targets, + }; + use lance_graph_codegraph::Codegraph; + + const BOTH: &[&str] = &["cg_lib", "cg_app"]; + + fn data() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/data/rev1") + } + + fn source_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../sag-ra-probe/fixtures/codegraph/rev1") + } + + /// A directory under the system temp dir, removed when the test ends. + struct Tmp(PathBuf); + + impl Tmp { + fn new(name: &str) -> Tmp { + let dir = + std::env::temp_dir().join(format!("lgcg_impact_{name}_{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + Tmp(dir) + } + } + + impl std::ops::Deref for Tmp { + type Target = Path; + fn deref(&self) -> &Path { + &self.0 + } + } + + impl Drop for Tmp { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } + } + + /// A store holding only `crates`, copied from rev1. + fn store(name: &str, crates: &[&str]) -> Tmp { + let dir = Tmp::new(name); + for c in crates { + for ext in ["rows.tsv", "files.tsv", "residuals.tsv"] { + std::fs::copy( + data().join(format!("{c}.{ext}")), + dir.join(format!("{c}.{ext}")), + ) + .unwrap(); + } + } + dir + } + + /// The fullest scope: every target extracted for every crate in `crates`, consumers + /// declared, freshness checked. + fn full(store: &Path, root: &Path, crates: &[&str], deps: &[&str]) -> Scope { + Scope { + store: Some(store.to_path_buf()), + source_root: Some(root.to_path_buf()), + targets: crates + .iter() + .map(|c| (c.to_string(), Targets::ALL)) + .collect(), + consumers: Consumers::Declared(deps.iter().map(|d| d.to_string()).collect()), + excluded: vec![], + } + } + + fn a_report(dir: &Path, symbol: &str, scope: &Scope) -> ImpactReport { + let r = Codegraph::open(dir) + .unwrap() + .impact_report(symbol, scope) + .unwrap(); + assert!( + matches!(r.symbol, SymbolState::Found(_)), + "{symbol} not found: {:?}", + r.symbol + ); + r + } + + fn confirmed(dir: &Path, r: &ImpactReport) -> Vec { + let g = Codegraph::open(dir).unwrap(); + let mut out = Vec::new(); + for (w, &word) in r.graded.confirmed.iter().enumerate() { + for b in 0..64 { + if word >> b & 1 == 1 { + out.push(g.symbol((w * 64 + b) as u32).unwrap().to_string()); + } + } + } + out.sort(); + out + } + + #[test] + fn no_callers_inside_a_fully_observed_scope_has_no_gap() { + // cg_app::run is public, but nothing depends on cg_app, so the declared scope is complete. + let dir = store("complete", BOTH); + let root = source_root(); + let r = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert!(confirmed(&dir, &r).is_empty()); + assert_eq!(r.gaps, vec![]); + assert!(r.closed_in_scope()); + } + + #[test] + fn a_public_symbol_with_unobserved_consumers_reports_missing_coverage() { + let dir = store("lib_only", &["cg_lib"]); + let root = source_root(); + let r = a_report( + &dir, + "cg_lib::util::double", + &full(&dir, &root, &["cg_lib"], &["cg_app"]), + ); + // The consumer has no position, so it is not in the answer: it is a gap, never an entry. + assert!(r.gaps.contains(&Gap::ConsumersNotInStore { + crates: vec!["cg_app".into()] + })); + assert!(!r.closed_in_scope()); + // Undeclared consumers of a public symbol are a gap too. + let undeclared = Scope { + consumers: Consumers::NotDeclared, + ..full(&dir, &root, &["cg_lib"], &[]) + }; + assert!(a_report(&dir, "cg_lib::util::double", &undeclared) + .gaps + .iter() + .any(|g| matches!(g, Gap::ExternalConsumersUnobserved { .. }))); + } + + #[test] + fn library_only_extraction_reports_unobserved_test_callers() { + let dir = store("targets", BOTH); + let root = source_root(); + let lib_only = Scope { + targets: BOTH + .iter() + .map(|c| (c.to_string(), Targets::LIB_ONLY)) + .collect(), + ..full(&dir, &root, BOTH, &[]) + }; + assert!(a_report(&dir, "cg_app::run", &lib_only) + .gaps + .iter() + .any(|g| matches!(g, Gap::TargetsNotExtracted { missing, .. } if missing.tests))); + assert!( + !a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])) + .gaps + .iter() + .any(|g| matches!(g, Gap::TargetsNotExtracted { .. })) + ); + } + + #[test] + fn dispatch_only_callers_report_unresolved_dispatch() { + // cg_lib::area_of dispatches Shape::area, which Rect implements; nothing calls Rect::area + // directly, so every caller is possible only. + let dir = store("dispatch", BOTH); + let root = source_root(); + let r = a_report( + &dir, + "cg_lib::shapes::impl_Shape_for_Rect::area", + &full(&dir, &root, BOTH, &[]), + ); + assert!(confirmed(&dir, &r).is_empty()); + assert!( + r.gaps + .iter() + .any(|g| matches!(g, Gap::TraitDispatch { possible } if *possible > 0)), + "{:?}", + r.gaps + ); + // Twin: util::triple's only caller (macro_use::checked) is reached by resolved calls only. + // (util::double would not do: Square::area calls it, and area_of reaches Square::area + // through dispatch, so area_of is a legitimate possible impact of double.) + let d = a_report(&dir, "cg_lib::util::triple", &full(&dir, &root, BOTH, &[])); + assert!( + !d.gaps + .iter() + .any(|g| matches!(g, Gap::TraitDispatch { .. })), + "{:?}", + d.gaps + ); + assert!(!confirmed(&dir, &d).is_empty()); + } + + #[test] + fn a_changed_source_file_reports_stale_coverage() { + let dir = store("stale", BOTH); + let root = Tmp::new("src"); + for c in ["cg_lib", "cg_app"] { + copy_dir(&source_root().join(c), &root.join(c)); + } + // cg_app depends on cg_lib, so cg_lib's sources decide the impact of a cg_lib symbol. + let scope = full(&dir, &root, BOTH, &["cg_app"]); + assert!(!a_report(&dir, "cg_lib::util::double", &scope) + .gaps + .iter() + .any(|g| matches!(g, Gap::StaleSource { .. }))); + let f = root.join("cg_lib/src/util.rs"); + let mut text = std::fs::read(&f).unwrap(); + text.extend_from_slice(b"\n// edited after extraction\n"); + std::fs::write(&f, text).unwrap(); + let stale = Gap::StaleSource { + krate: "cg_lib".into(), + files: vec!["cg_lib/src/util.rs".into()], + }; + let r = a_report(&dir, "cg_lib::util::double", &scope); + assert!(r.gaps.contains(&stale), "{:?}", r.gaps); + // Declared scope narrows it: nothing depends on cg_app, so no cg_lib code can call + // cg_app::run and cg_lib's edit cannot move that answer. + let none = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert!(!none.gaps.contains(&stale), "{:?}", none.gaps); + // Without declared consumers any stored crate may call the public cg_app::run, so the + // same edit is a gap again. + let undeclared = Scope { + consumers: Consumers::NotDeclared, + ..full(&dir, &root, BOTH, &[]) + }; + let open = a_report(&dir, "cg_app::run", &undeclared); + assert!(open.gaps.contains(&stale), "{:?}", open.gaps); + // No source root: freshness is not checked, and that is a gap, not a pass. + let unchecked = Scope { + store: None, + source_root: None, + ..full(&dir, &root, BOTH, &[]) + }; + assert!(a_report(&dir, "cg_app::run", &unchecked) + .gaps + .contains(&Gap::FreshnessUnchecked)); + } + + #[test] + fn a_deleted_source_file_is_missing_not_stale() { + let dir = store("deleted", BOTH); + let root = Tmp::new("src_deleted"); + for c in ["cg_lib", "cg_app"] { + copy_dir(&source_root().join(c), &root.join(c)); + } + let scope = full(&dir, &root, BOTH, &["cg_app"]); + let any_file_gap = |r: &ImpactReport| { + r.gaps + .iter() + .any(|g| matches!(g, Gap::MissingSource { .. } | Gap::StaleSource { .. })) + }; + assert!(!any_file_gap(&a_report( + &dir, + "cg_lib::util::double", + &scope + ))); + std::fs::remove_file(root.join("cg_lib/src/util.rs")).unwrap(); + let r = a_report(&dir, "cg_lib::util::double", &scope); + assert!( + r.gaps.contains(&Gap::MissingSource { + krate: "cg_lib".into(), + files: vec!["cg_lib/src/util.rs".into()] + }), + "{:?}", + r.gaps + ); + // Deleted is not edited: the two are separate gaps. + assert!(!r.gaps.iter().any(|g| matches!(g, Gap::StaleSource { .. }))); + } + + #[test] + fn a_declared_consumer_without_a_caller_still_needs_its_own_coverage() { + // cg_lib::util::triple has no caller in cg_app. A declared consumer may still call it + // from code it did not extract, so its targets count. + let dir = store("consumer_coverage", BOTH); + let root = source_root(); + let lib_only_declared = |deps: &[&str]| Scope { + targets: vec![("cg_lib".to_string(), Targets::ALL)], + ..full(&dir, &root, BOTH, deps) + }; + let undeclared = Gap::TargetsUndeclared { + krate: "cg_app".into(), + }; + let with = a_report( + &dir, + "cg_lib::util::triple", + &lib_only_declared(&["cg_app"]), + ); + assert!( + !confirmed(&dir, &with) + .iter() + .any(|s| s.starts_with("cg_app::")), + "fixture changed: cg_app now calls triple" + ); + assert!(with.gaps.contains(&undeclared), "{:?}", with.gaps); + let without = a_report(&dir, "cg_lib::util::triple", &lib_only_declared(&[])); + assert!(!without.gaps.contains(&undeclared), "{:?}", without.gaps); + } + + #[test] + fn a_symbol_absent_from_the_store_is_not_an_empty_impact() { + let dir = store("missing", &["cg_lib"]); + let root = source_root(); + let r = Codegraph::open(&dir) + .unwrap() + .impact_report("cg_app::run", &full(&dir, &root, &["cg_lib"], &[])) + .unwrap(); + // cg_app is not a stored crate: nothing of it was read. + assert_eq!( + r.symbol, + SymbolState::Missing { + owner_stored: false + } + ); + assert_eq!( + r.observed + .iter() + .map(|(c, _)| c.as_str()) + .collect::>(), + vec!["cg_lib"] + ); + assert!(!r.closed_in_scope()); + } + + #[test] + fn adding_the_consumer_crate_upgrades_missing_coverage_to_confirmed() { + let root = source_root(); + let lib = store("before", &["cg_lib"]); + let before = a_report( + &lib, + "cg_lib::util::double", + &full(&lib, &root, &["cg_lib"], &["cg_app"]), + ); + assert!(!confirmed(&lib, &before).contains(&"cg_app::run".to_string())); + assert!(before.gaps.contains(&Gap::ConsumersNotInStore { + crates: vec!["cg_app".into()] + })); + + let both = store("after", BOTH); + let after = a_report( + &both, + "cg_lib::util::double", + &full(&both, &root, BOTH, &["cg_app"]), + ); + assert!( + confirmed(&both, &after).contains(&"cg_app::run".to_string()), + "{:?}", + confirmed(&both, &after) + ); + assert!(!after + .gaps + .iter() + .any(|g| matches!(g, Gap::ConsumersNotInStore { .. }))); + // The unchanged position-mask answer is a superset of what it was. + assert!(confirmed(&lib, &before) + .iter() + .all(|s| confirmed(&both, &after).contains(s))); + } + + fn copy_dir(from: &Path, to: &Path) { + std::fs::create_dir_all(to).unwrap(); + for e in std::fs::read_dir(from).unwrap() { + let e = e.unwrap(); + let p = e.path(); + if p.is_dir() { + copy_dir(&p, &to.join(e.file_name())); + } else { + std::fs::copy(&p, to.join(e.file_name())).unwrap(); + } + } + } + + /// Fails if extraction residuals are dropped from the impact answer: a body ruff_charon could + /// not represent may call the symbol, so a crate with residuals is a gap even when its CRATE row + /// is `COMPLETE`. The twin is `no_callers_inside_a_fully_observed_scope_has_no_gap` (rev1 has + /// none). + #[test] + fn extraction_residuals_are_a_coverage_gap() { + let dir = store("residuals", BOTH); + let root = source_root(); + let res = dir.join("cg_app.residuals.tsv"); + let mut t = std::fs::read_to_string(&res).unwrap(); + t += "BODY_NOT_STRUCTURED\tcg_app::run\n"; + std::fs::write(&res, t).unwrap(); + let r = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert!( + matches!( + r.gaps.as_slice(), + [Gap::HarvestIncomplete { + krate, + residuals, + partial: false, + no_residuals_file: false + }] if krate == "cg_app" && residuals.total() == 1 + ), + "{:?}", + r.gaps + ); + assert!(!r.closed_in_scope()); + } + + /// Fails if a crate read without a residuals file counts as fully covered: nothing is known + /// about what its harvest missed. + #[test] + fn a_missing_residuals_file_is_a_coverage_gap() { + let dir = store("no_residuals", BOTH); + let root = source_root(); + std::fs::remove_file(dir.join("cg_app.residuals.tsv")).unwrap(); + let r = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert!( + matches!( + r.gaps.as_slice(), + [Gap::HarvestIncomplete { + krate, + no_residuals_file: true, + .. + }] if krate == "cg_app" + ), + "{:?}", + r.gaps + ); + } + + /// Fails if a crate read without a CRATE row is left out of the report: nothing states that its + /// extraction completed, so it is a partial crate, and its residual and freshness checks still run. + #[test] + fn a_crate_without_a_crate_row_is_partial_not_absent() { + let dir = store("no_crate_row", BOTH); + let root = source_root(); + let rows = dir.join("cg_app.rows.tsv"); + let text = std::fs::read_to_string(&rows).unwrap(); + let kept: Vec<&str> = text.lines().filter(|l| !l.starts_with("CRATE\t")).collect(); + assert_eq!( + kept.len() + 1, + text.lines().count(), + "exactly one CRATE row" + ); + std::fs::write(&rows, kept.join("\n") + "\n").unwrap(); + let r = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert!( + r.observed.iter().any(|(c, _)| c == "cg_app"), + "{:?}", + r.observed + ); + assert!( + matches!( + r.gaps.as_slice(), + [Gap::HarvestIncomplete { + krate, + partial: true, + .. + }] if krate == "cg_app" + ), + "{:?}", + r.gaps + ); + } + + /// Fails if a symbol whose rows a refresh removed still answers `Found` with an empty impact: its + /// position survives the refresh, but it is no longer in the artifacts. + #[test] + fn a_symbol_removed_by_a_refresh_is_not_found() { + let dir = store("removed", BOTH); + let root = source_root(); + let mut g = Codegraph::open(&dir).unwrap(); + let scope = full(&dir, &root, BOTH, &[]); + assert!(matches!( + g.impact_report("cg_app::run", &scope).unwrap().symbol, + SymbolState::Found(_) + )); + let rows = dir.join("cg_app.rows.tsv"); + let text = std::fs::read_to_string(&rows).unwrap(); + let kept: Vec<&str> = text + .lines() + .filter(|l| l.starts_with('#') || l.starts_with("CRATE\t")) + .collect(); + std::fs::write(&rows, kept.join("\n") + "\n").unwrap(); + g.refresh_crate(&dir, "cg_app").unwrap(); + assert!(g.pos("cg_app::run").is_some(), "the position is retained"); + assert!(matches!( + g.impact_report("cg_app::run", &scope).unwrap().symbol, + SymbolState::Missing { .. } + )); + } + + /// Fails if a file-list row without a hash passes as a successful freshness check: that source + /// file was never compared. + #[test] + fn a_malformed_file_list_row_is_a_freshness_gap() { + let dir = store("bad_files", BOTH); + let root = source_root(); + let files = dir.join("cg_app.files.tsv"); + let mut t = std::fs::read_to_string(&files).unwrap(); + t += "cg_app/src/extra.rs\n"; + std::fs::write(&files, t).unwrap(); + let r = a_report(&dir, "cg_app::run", &full(&dir, &root, BOTH, &[])); + assert_eq!( + r.gaps, + vec![Gap::InvalidFileList { + krate: "cg_app".into() + }] + ); + } +} + +mod suite_b { + use std::path::PathBuf; + + use lance_graph_codegraph::impact_report::{Consumers, Gap, Scope, SymbolState, Targets}; + use lance_graph_codegraph::Codegraph; + use lance_graph_contract::hash::fnv1a; + + pub const HEADER: &str = + "#version 2\n#schema predicate\tsubject\tobject\tgrade\tfile\tstart\tend\n"; + pub const RES_HEADER: &str = "#version 1\n#schema reason\tsubject\n"; + pub const FILES_HEADER: &str = "#version 1\n#schema path\tfnv64\n"; + + fn fnv64_hex(bytes: &[u8]) -> String { + format!("{:016x}", fnv1a(bytes)) + } + + /// One crate of a store: its rows (predicate, subject, object) and the source + /// files it was extracted from (path, contents). + pub struct Krate<'a> { + pub name: &'a str, + pub rows: &'a [(&'a str, &'a str, &'a str)], + pub files: &'a [(&'a str, &'a str)], + } + + /// A store directory plus a source root holding each crate's files. + pub struct Fixture { + pub dir: PathBuf, + pub store: PathBuf, + pub src: PathBuf, + } + + impl Fixture { + pub fn new(name: &str, crates: &[Krate<'_>]) -> Fixture { + let dir = + std::env::temp_dir().join(format!("lgcg_impact_{name}_{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let (store, src) = (dir.join("store"), dir.join("src")); + std::fs::create_dir_all(&store).unwrap(); + std::fs::create_dir_all(&src).unwrap(); + let fx = Fixture { dir, store, src }; + for k in crates { + fx.add(k); + } + fx + } + + /// Write one crate's rows, empty residuals and files table. + pub fn add(&self, k: &Krate<'_>) { + let file = k.files.first().map_or("f.rs", |f| f.0); + let mut t = String::from(HEADER); + for (p, s, o) in k.rows { + t += &format!("{p}\t{s}\t{o}\tCOMPILER\t{file}\t1\t2\n"); + } + t += &format!("CRATE\t{}\tCOMPLETE\tCOMPILER\t-\t-\t-\n", k.name); + std::fs::write(self.store.join(format!("{}.rows.tsv", k.name)), t).unwrap(); + std::fs::write( + self.store.join(format!("{}.residuals.tsv", k.name)), + RES_HEADER, + ) + .unwrap(); + let mut f = String::from(FILES_HEADER); + for (path, body) in k.files { + let at = self.src.join(path); + std::fs::create_dir_all(at.parent().unwrap()).unwrap(); + std::fs::write(at, body).unwrap(); + f += &format!("{path}\t{}\n", fnv64_hex(body.as_bytes())); + } + std::fs::write(self.store.join(format!("{}.files.tsv", k.name)), f).unwrap(); + } + + pub fn open(&self) -> Codegraph { + Codegraph::open(&self.store).unwrap() + } + + /// Every stored crate fully extracted, freshness checked against `src`. + pub fn scope(&self, crates: &[&str]) -> Scope { + Scope { + store: Some(self.store.clone()), + source_root: Some(self.src.clone()), + targets: crates + .iter() + .map(|c| (c.to_string(), Targets::ALL)) + .collect(), + consumers: Consumers::NotDeclared, + excluded: Vec::new(), + } + } + } + + impl Drop for Fixture { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.dir); + } + } + + /// `lib::entry` is public and calls `lib::used`; `lib::helper` is private + /// and has no caller. + pub const LIB: &[(&str, &str, &str)] = &[ + ("DEFINES", "lib::entry", "lib::entry"), + ("PUBLIC", "lib::entry", "lib::entry"), + ("DEFINES", "lib::used", "lib::used"), + ("DEFINES", "lib::helper", "lib::helper"), + ("CALLS_RESOLVED", "lib::entry", "lib::used"), + ]; + + pub fn lib_crate() -> Krate<'static> { + Krate { + name: "lib", + rows: LIB, + files: &[( + "lib.rs", + "pub fn entry() { used() }\nfn used() {}\nfn helper() {}\n", + )], + } + } + + pub fn has(g: &Codegraph, mask: &[u64], symbol: &str) -> bool { + g.pos(symbol).is_some_and(|p| { + mask.get(p as usize / 64) + .is_some_and(|w| w >> (p % 64) & 1 == 1) + }) + } + + pub fn empty(mask: &[u64]) -> bool { + mask.iter().all(|&w| w == 0) + } + + /// Case 1: A private symbol with no caller, in a crate whose every target was + /// extracted, whose harvest left no residual and whose sources are unchanged: + /// the empty answer is closed in scope. Anti-vacuity: `lib::used` does have a + /// caller, so the impact query is live, not merely empty. + #[test] + fn no_callers_in_a_fully_observed_scope_is_closed() { + let fx = Fixture::new("closed", &[lib_crate()]); + let g = fx.open(); + let r = g.impact_report("lib::helper", &fx.scope(&["lib"])).unwrap(); + assert!( + empty(&r.graded.confirmed) && empty(&r.graded.possible) && empty(&r.graded.unknown) + ); + assert_eq!(r.gaps, Vec::::new()); + assert!(r.closed_in_scope()); + + let used = g.impact_report("lib::used", &fx.scope(&["lib"])).unwrap(); + assert!(has(&g, &used.graded.confirmed, "lib::entry")); + } + + /// Case 2: A public symbol with no observed caller: the masks stay exactly what + /// `impact` returns (no fabricated unknown), and the missing external + /// callers are a typed gap. `Coverage::no_known_gap()` is true for this + /// store, and the report is still not closed: no-known-gap is not global + /// completeness. + #[test] + fn a_public_symbol_reports_its_unobserved_external_consumers() { + let fx = Fixture::new("public", &[lib_crate()]); + let g = fx.open(); + assert!(g.coverage().no_known_gap()); + let pos = g.pos("lib::entry").unwrap(); + let r = g.impact_report("lib::entry", &fx.scope(&["lib"])).unwrap(); + assert_eq!(r.graded, g.impact(pos).unwrap()); + assert!( + empty(&r.graded.unknown), + "missing coverage must not become a position" + ); + assert_eq!( + r.gaps, + vec![Gap::ExternalConsumersUnobserved { + owner: "lib".into(), + observed_consumers: vec![], + }] + ); + assert!(!r.closed_in_scope()); + + // Silence twin: the caller declares the consumer set closed. + let closed = Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib"]) + }; + assert!(g + .impact_report("lib::entry", &closed) + .unwrap() + .closed_in_scope()); + } + + /// Case 3: Test-target callers: with only the lib target extracted, the + /// test-only caller has no position, so the report names the missing + /// targets. With the test target extracted (its rows present, declared ALL), + /// the caller is confirmed and the gap disappears. + #[test] + fn test_callers_absent_from_a_lib_extraction_are_a_gap() { + let fx = Fixture::new("tests", &[lib_crate()]); + let lib_only = Scope { + targets: vec![("lib".into(), Targets::LIB_ONLY)], + ..fx.scope(&["lib"]) + }; + let r = fx.open().impact_report("lib::helper", &lib_only).unwrap(); + assert!(empty(&r.graded.confirmed)); + assert_eq!( + r.gaps, + vec![Gap::TargetsNotExtracted { + krate: "lib".into(), + missing: Targets { + lib: false, + tests: true, + examples: true, + benches: true, + }, + }] + ); + + // An undeclared crate is not assumed complete. + let undeclared = Scope { + targets: vec![], + ..fx.scope(&["lib"]) + }; + let r = fx.open().impact_report("lib::helper", &undeclared).unwrap(); + assert_eq!( + r.gaps, + vec![Gap::TargetsUndeclared { + krate: "lib".into() + }] + ); + + // The test target extracted: the caller is a confirmed position. + let mut rows = LIB.to_vec(); + rows.push(( + "DEFINES", + "lib::tests::helper_works", + "lib::tests::helper_works", + )); + rows.push(("CALLS_RESOLVED", "lib::tests::helper_works", "lib::helper")); + let fx2 = Fixture::new( + "tests_all", + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ); + let g = fx2.open(); + let r = g + .impact_report("lib::helper", &fx2.scope(&["lib"])) + .unwrap(); + assert!(has(&g, &r.graded.confirmed, "lib::tests::helper_works")); + assert_eq!(r.gaps, Vec::::new()); + } + + /// Case 4: Unresolved dispatch: `lib::cb` is referenced (its address is taken) and + /// `lib::runner` makes a call the compiler could not name, so a dynamic call + /// may reach `cb`. Silence twin: `lib::used` is neither an impl nor + /// referenced, so the same dynamic call is not a gap for it. + #[test] + fn an_unresolved_dispatch_that_may_reach_the_symbol_is_a_gap() { + let mut rows = LIB.to_vec(); + rows.extend([ + ("DEFINES", "lib::cb", "lib::cb"), + ("DEFINES", "lib::runner", "lib::runner"), + ("REFERENCES", "lib::entry", "lib::cb"), + ("UNRESOLVED_CALL", "lib::runner", "dynamic"), + ]); + let fx = Fixture::new( + "dyn", + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ); + let g = fx.open(); + let r = g.impact_report("lib::cb", &fx.scope(&["lib"])).unwrap(); + assert!( + r.gaps.iter().any(|x| matches!( + x, + Gap::DynamicCalls { + store_dynamic_calls: 1, + .. + } + )), + "{:?}", + r.gaps + ); + + let quiet = g.impact_report("lib::used", &fx.scope(&["lib"])).unwrap(); + assert!(!quiet + .gaps + .iter() + .any(|x| matches!(x, Gap::DynamicCalls { .. }))); + } + + /// Case 5: Stale source: a recorded file changed after extraction. Twins: the + /// unchanged tree has no gap; a missing files table and a missing source + /// root are their own gaps, never "fresh". + #[test] + fn a_dynamic_call_from_an_already_confirmed_caller_is_still_a_gap() { + // runner calls cb directly AND holds a dynamic call; cb is referenced, so that dynamic + // call may also target it. runner is confirmed through the direct call, which empties + // `graded.unknown` without resolving where the dynamic call goes. + let mut rows = LIB.to_vec(); + rows.extend([ + ("DEFINES", "lib::cb", "lib::cb"), + ("DEFINES", "lib::runner", "lib::runner"), + ("REFERENCES", "lib::entry", "lib::cb"), + ("CALLS_RESOLVED", "lib::runner", "lib::cb"), + ("UNRESOLVED_CALL", "lib::runner", "dynamic"), + ]); + let fx = Fixture::new( + "dyn_confirmed", + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ); + let g = fx.open(); + let r = g.impact_report("lib::cb", &fx.scope(&["lib"])).unwrap(); + assert!(has(&g, &r.graded.confirmed, "lib::runner")); + assert!( + empty(&r.graded.unknown), + "fixture no longer isolates the case" + ); + assert!( + r.gaps.contains(&Gap::DynamicCalls { + unknown: 0, + store_dynamic_calls: 1 + }), + "{:?}", + r.gaps + ); + assert!(!r.closed_in_scope()); + } + + /// PR 2004 review: the queried function is neither an impl nor referenced, but its caller + /// `lib::m` is an impl. `lib::runner` calls `lib::m` directly and holds a dynamic call that + /// may also dispatch to `lib::m`. + #[test] + fn a_dynamic_call_that_may_reach_a_caller_of_the_symbol_is_a_gap() { + let mut rows = LIB.to_vec(); + rows.extend([ + ("DEFINES", "lib::g", "lib::g"), + ("DEFINES", "lib::m", "lib::m"), + ("DEFINES", "lib::runner", "lib::runner"), + ("IMPLEMENTS", "lib::m", "lib::Tr::m"), + ("CALLS_RESOLVED", "lib::m", "lib::g"), + ("CALLS_RESOLVED", "lib::runner", "lib::m"), + ("UNRESOLVED_CALL", "lib::runner", "dynamic"), + ]); + let fx = Fixture::new( + "dyn_caller_impl", + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ); + let g = fx.open(); + let r = g.impact_report("lib::g", &fx.scope(&["lib"])).unwrap(); + assert!(has(&g, &r.graded.confirmed, "lib::runner")); + assert!( + empty(&r.graded.unknown), + "fixture no longer isolates the case" + ); + assert!( + r.gaps.contains(&Gap::DynamicCalls { + unknown: 0, + store_dynamic_calls: 1 + }), + "{:?}", + r.gaps + ); + assert!(!r.closed_in_scope()); + } + + /// `lib::r` takes the address of `lib::g` and never calls it. `with_dynamic` adds a caller + /// `lib::d` that also holds a dynamic call; `r_calls` makes `lib::r` a real caller. + fn referrer_store(tag: &str, with_dynamic: bool, r_calls: bool) -> Fixture { + let mut rows = vec![ + ("DEFINES", "lib::g", "lib::g"), + ("DEFINES", "lib::r", "lib::r"), + ("REFERENCES", "lib::r", "lib::g"), + ]; + if with_dynamic { + rows.extend([ + ("DEFINES", "lib::d", "lib::d"), + ("CALLS_RESOLVED", "lib::d", "lib::g"), + ("UNRESOLVED_CALL", "lib::d", "dynamic"), + ]); + } + if r_calls { + rows.push(("CALLS_RESOLVED", "lib::r", "lib::g")); + } + Fixture::new( + tag, + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ) + } + + fn closed_scope(fx: &Fixture) -> Scope { + Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib"]) + } + } + + #[test] + fn a_function_that_only_takes_the_address_is_a_gap() { + // The referrer is in no mask. The report must not be closed, with or without a dynamic + // call elsewhere in the store. + for (tag, with_dynamic) in [("ref_dyn", true), ("ref_plain", false)] { + let fx = referrer_store(tag, with_dynamic, false); + let g = fx.open(); + let r = g.impact_report("lib::g", &closed_scope(&fx)).unwrap(); + let pos = g.pos("lib::g").unwrap(); + assert_eq!(r.graded, g.impact(pos).unwrap(), "{tag}"); + for m in [&r.graded.confirmed, &r.graded.possible, &r.graded.unknown] { + assert!( + !has(&g, m, "lib::r"), + "{tag}: the referrer must not be graded" + ); + } + assert!( + r.gaps + .contains(&Gap::ReferrersOutsideAnswer { referrers: 1 }), + "{tag}: {:?}", + r.gaps + ); + assert!(!r.closed_in_scope(), "{tag}"); + } + } + + #[test] + fn a_referrer_that_also_calls_is_in_the_answer_and_no_gap() { + let fx = referrer_store("ref_calls", false, true); + let g = fx.open(); + let r = g.impact_report("lib::g", &closed_scope(&fx)).unwrap(); + assert!(has(&g, &r.graded.confirmed, "lib::r")); + assert!( + !r.gaps + .iter() + .any(|x| matches!(x, Gap::ReferrersOutsideAnswer { .. })), + "{:?}", + r.gaps + ); + assert!(r.closed_in_scope(), "{:?}", r.gaps); + } + + #[test] + fn only_references_into_the_answer_are_counted() { + // lib::r references lib::other, which is not in lib::g's answer; lib::x has a row of + // another predicate pointing at lib::g. Neither is a referrer of the answer. + let rows = [ + ("DEFINES", "lib::g", "lib::g"), + ("DEFINES", "lib::r", "lib::r"), + ("DEFINES", "lib::other", "lib::other"), + ("DEFINES", "lib::x", "lib::x"), + ("REFERENCES", "lib::r", "lib::other"), + ("ANNOTATES", "lib::x", "lib::g"), + ]; + let fx = Fixture::new( + "ref_silence", + &[Krate { + name: "lib", + rows: &rows, + files: lib_crate().files, + }], + ); + let g = fx.open(); + let r = g.impact_report("lib::g", &closed_scope(&fx)).unwrap(); + assert!( + !r.gaps + .iter() + .any(|x| matches!(x, Gap::ReferrersOutsideAnswer { .. })), + "{:?}", + r.gaps + ); + // The same rows do count for the symbol they point at. + let other = g.impact_report("lib::other", &closed_scope(&fx)).unwrap(); + assert!(other + .gaps + .contains(&Gap::ReferrersOutsideAnswer { referrers: 1 })); + } + + #[test] + fn undeclared_consumers_inspect_every_stored_crate_without_fabricating_impact() { + // `app` has no edge to `lib::entry`, is stale, lib-only and has an excluded region. + let fx = Fixture::new("every_crate", &[lib_crate()]); + fx.add(&Krate { + name: "app", + rows: &[("DEFINES", "app::main", "app::main")], + files: &[("app_main.rs", "fn main() {}\n")], + }); + std::fs::write(fx.src.join("app_main.rs"), "fn main() { changed() }\n").unwrap(); + let scope = Scope { + targets: vec![ + ("lib".to_string(), Targets::ALL), + ("app".to_string(), Targets::LIB_ONLY), + ], + excluded: vec![("app".to_string(), vec!["feature:x".to_string()])], + ..fx.scope(&[]) + }; + let g = fx.open(); + let r = g.impact_report("lib::entry", &scope).unwrap(); + assert_eq!(r.graded, g.impact(g.pos("lib::entry").unwrap()).unwrap()); + let names_app = |want: &dyn Fn(&Gap) -> bool| r.gaps.iter().any(want); + assert!(names_app( + &|x| matches!(x, Gap::StaleSource { krate, .. } if krate == "app") + )); + assert!(names_app( + &|x| matches!(x, Gap::TargetsNotExtracted { krate, .. } if krate == "app") + )); + assert!(names_app( + &|x| matches!(x, Gap::RegionsExcluded { krate, .. } if krate == "app") + )); + } + + #[test] + fn a_stale_source_revision_is_a_gap() { + let fx = Fixture::new("stale", &[lib_crate()]); + let g = fx.open(); + assert!(g + .impact_report("lib::helper", &fx.scope(&["lib"])) + .unwrap() + .gaps + .is_empty()); + + // The file is edited, not deleted: its digest differs, so it is stale, not missing. + std::fs::write( + fx.src.join("lib.rs"), + "pub fn entry() { used(); helper() }\n", + ) + .unwrap(); + let r = g.impact_report("lib::helper", &fx.scope(&["lib"])).unwrap(); + assert_eq!( + r.gaps, + vec![Gap::StaleSource { + krate: "lib".into(), + files: vec!["lib.rs".into()], + }] + ); + + let unchecked = Scope { + source_root: None, + ..fx.scope(&["lib"]) + }; + assert_eq!( + g.impact_report("lib::helper", &unchecked).unwrap().gaps, + vec![Gap::FreshnessUnchecked] + ); + + std::fs::remove_file(fx.store.join("lib.files.tsv")).unwrap(); + assert_eq!( + g.impact_report("lib::helper", &fx.scope(&["lib"])) + .unwrap() + .gaps, + vec![Gap::FilesUnrecorded { + krate: "lib".into() + }] + ); + } + + /// Case 6: A truly missing symbol is not "no callers": it is `Missing`, never + /// closed, and says whether its crate was read at all. + #[test] + fn a_missing_symbol_is_missing_not_unaffected() { + let fx = Fixture::new("missing", &[lib_crate()]); + let g = fx.open(); + let scope = fx.scope(&["lib"]); + let r = g.impact_report("lib::nope", &scope).unwrap(); + assert_eq!(r.symbol, SymbolState::Missing { owner_stored: true }); + assert!(!r.closed_in_scope()); + let r = g.impact_report("other::thing", &scope).unwrap(); + assert_eq!( + r.symbol, + SymbolState::Missing { + owner_stored: false + } + ); + assert!(!r.closed_in_scope()); + // Twin: a present symbol with no callers is Found. + assert!(matches!( + g.impact_report("lib::helper", &scope).unwrap().symbol, + SymbolState::Found(_) + )); + } + + /// Case 7: Adding a consumer crate upgrades the answer: before, `lib::entry` has no + /// confirmed caller and the external callers are unobserved; after the `app` + /// crate's compiler facts are stored, `app::main` is a confirmed position and + /// the gap names `app` as an observed consumer. The gap stays until the + /// consumer set is declared closed, and `app` must itself be declared. + #[test] + fn adding_a_consumer_crate_confirms_its_callers() { + let fx = Fixture::new("consumer", &[lib_crate()]); + let before = fx + .open() + .impact_report("lib::entry", &fx.scope(&["lib"])) + .unwrap(); + assert!(empty(&before.graded.confirmed)); + + fx.add(&Krate { + name: "app", + rows: &[ + ("DEFINES", "app::main", "app::main"), + ("CALLS_RESOLVED", "app::main", "lib::entry"), + ], + files: &[("main.rs", "fn main() { lib::entry() }\n")], + }); + let g = fx.open(); + let after = g + .impact_report("lib::entry", &fx.scope(&["lib", "app"])) + .unwrap(); + assert!(has(&g, &after.graded.confirmed, "app::main")); + assert_eq!( + after.gaps, + vec![Gap::ExternalConsumersUnobserved { + owner: "lib".into(), + observed_consumers: vec!["app".into()], + }] + ); + + let closed = Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib", "app"]) + }; + assert!(g + .impact_report("lib::entry", &closed) + .unwrap() + .closed_in_scope()); + + let app_undeclared = Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib"]) + }; + assert_eq!( + g.impact_report("lib::entry", &app_undeclared).unwrap().gaps, + vec![Gap::TargetsUndeclared { + krate: "app".into() + }] + ); + } + + /// Case 8: An incomplete harvest of the owning crate (a residual, a missing + /// residuals file, or a PARTIAL crate row) is a gap: absent rows prove + /// nothing. Twin: the same store with empty residuals has no such gap (test 1). + #[test] + fn an_incomplete_harvest_of_the_owner_is_a_gap() { + let fx = Fixture::new("harvest", &[lib_crate()]); + std::fs::write( + fx.store.join("lib.residuals.tsv"), + format!("{RES_HEADER}BODY_NOT_STRUCTURED\tlib::entry\n"), + ) + .unwrap(); + let r = fx + .open() + .impact_report("lib::helper", &fx.scope(&["lib"])) + .unwrap(); + assert!( + matches!(r.gaps.as_slice(), [Gap::HarvestIncomplete { krate, residuals, partial: false, no_residuals_file: false }] + if krate == "lib" && residuals.total() == 1), + "{:?}", + r.gaps + ); + + std::fs::remove_file(fx.store.join("lib.residuals.tsv")).unwrap(); + let r = fx + .open() + .impact_report("lib::helper", &fx.scope(&["lib"])) + .unwrap(); + assert!( + matches!( + r.gaps.as_slice(), + [Gap::HarvestIncomplete { + no_residuals_file: true, + .. + }] + ), + "{:?}", + r.gaps + ); + } + + /// Case 9: Regions the extraction did not translate (an opaque module, a + /// cargo feature left off) are a gap for a found symbol and for a missing + /// one. A symbol defined only in an uncompiled feature module is missing from + /// the store without being absent from the crate. + #[test] + fn excluded_regions_are_a_gap_for_found_and_missing_symbols() { + let fx = Fixture::new("excluded", &[lib_crate()]); + let g = fx.open(); + let excluded = Scope { + excluded: vec![("lib".into(), vec!["feature iam".into()])], + ..fx.scope(&["lib"]) + }; + let gap = Gap::RegionsExcluded { + krate: "lib".into(), + regions: vec!["feature iam".into()], + }; + let r = g.impact_report("lib::helper", &excluded).unwrap(); + assert_eq!(r.gaps, vec![gap.clone()]); + assert!(!r.closed_in_scope()); + let r = g.impact_report("lib::authorize", &excluded).unwrap(); + assert_eq!(r.symbol, SymbolState::Missing { owner_stored: true }); + assert_eq!(r.gaps, vec![gap]); + // Silence twins: no exclusion for this crate, or an empty list, leaves the + // found symbol closed and the missing one without gaps. + for silent in [ + fx.scope(&["lib"]), + Scope { + excluded: vec![ + ("lib".into(), Vec::new()), + ("other".into(), vec!["x".into()]), + ], + ..fx.scope(&["lib"]) + }, + ] { + assert!(g + .impact_report("lib::helper", &silent) + .unwrap() + .closed_in_scope()); + assert!(g + .impact_report("lib::authorize", &silent) + .unwrap() + .gaps + .is_empty()); + } + } + + /// Case 10: A missing symbol carries its crate's target gaps. Extracting + /// only the library cannot show that a symbol is absent from the tests. + #[test] + fn a_missing_symbol_carries_its_crate_target_gaps() { + let fx = Fixture::new("missing-targets", &[lib_crate()]); + let g = fx.open(); + let lib_only = Scope { + targets: vec![("lib".into(), Targets::LIB_ONLY)], + ..fx.scope(&["lib"]) + }; + let r = g.impact_report("lib::only_in_tests", &lib_only).unwrap(); + assert!( + matches!( + r.gaps.as_slice(), + [Gap::TargetsNotExtracted { krate, missing }] if krate == "lib" && missing.tests + ), + "{:?}", + r.gaps + ); + // A symbol outside every stored crate reports no crate gap: nothing of + // its crate was read, which `owner_stored: false` already says. + assert!(g + .impact_report("other::x", &lib_only) + .unwrap() + .gaps + .is_empty()); + } + + /// Case 11: A `.rs` file under a directory the extraction read from, but not + /// itself recorded, is a gap: its code (a feature-gated module, a binary + /// root) has no rows. Files in the root and non-Rust files are not scanned. + #[test] + fn unread_source_files_under_a_read_directory_are_a_gap() { + let k = Krate { + name: "lib", + rows: &[ + ("DEFINES", "lib::entry", "lib::entry"), + ("DEFINES", "lib::helper", "lib::helper"), + ], + files: &[("crate/lib.rs", "fn entry() {}\nfn helper() {}\n")], + }; + let fx = Fixture::new("unread", &[k]); + let g = fx.open(); + let scope = fx.scope(&["lib"]); + // Silence twin: everything under crate/ is recorded; a non-Rust file and a + // root-level file are not scanned. + std::fs::write(fx.src.join("crate/data.json"), "{}").unwrap(); + std::fs::write(fx.src.join("top.rs"), "fn x() {}").unwrap(); + assert!(g + .impact_report("lib::helper", &scope) + .unwrap() + .closed_in_scope()); + std::fs::create_dir_all(fx.src.join("crate/gated")).unwrap(); + std::fs::write(fx.src.join("crate/iam.rs"), "pub fn authorize() {}").unwrap(); + std::fs::write(fx.src.join("crate/gated/deep.rs"), "fn d() {}").unwrap(); + let r = g.impact_report("lib::helper", &scope).unwrap(); + assert_eq!( + r.gaps, + vec![Gap::SourceFilesUnread { + krate: "lib".into(), + files: vec!["crate/gated/deep.rs".into(), "crate/iam.rs".into()], + }] + ); + // A symbol missing from the store may be defined in an unread file. + let r = g.impact_report("lib::authorize", &scope).unwrap(); + assert_eq!(r.symbol, SymbolState::Missing { owner_stored: true }); + assert!( + matches!(r.gaps.as_slice(), [Gap::SourceFilesUnread { files, .. }] if files.len() == 2), + "{:?}", + r.gaps + ); + } + + /// Case 12: A private symbol reached by a public caller is affected by that + /// caller's external consumers, so the external-consumer gap fires for it + /// too. A private symbol no public function reaches stays closed. + #[test] + fn a_private_symbol_behind_a_public_caller_has_unobserved_consumers() { + let fx = Fixture::new("frontier", &[lib_crate()]); + let g = fx.open(); + let r = g.impact_report("lib::used", &fx.scope(&["lib"])).unwrap(); + assert!(has(&g, &r.graded.confirmed, "lib::entry")); + assert!( + matches!(r.gaps.as_slice(), [Gap::ExternalConsumersUnobserved { owner, .. }] if owner == "lib"), + "{:?}", + r.gaps + ); + assert!(!r.closed_in_scope()); + // Silence twins: nothing public reaches `lib::helper`; and a closed + // consumer set closes `lib::used`. + assert!(g + .impact_report("lib::helper", &fx.scope(&["lib"])) + .unwrap() + .closed_in_scope()); + let closed = Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib"]) + }; + assert!(g + .impact_report("lib::used", &closed) + .unwrap() + .closed_in_scope()); + } + + /// Case 13: With the consumer set declared closed, every stored crate is a + /// possible consumer. One whose call into the symbol sits in an excluded + /// region contributes no position, and its exclusion must still be reported. + #[test] + fn a_closed_consumer_set_checks_consumers_without_observed_edges() { + let app = Krate { + name: "app", + rows: &[("DEFINES", "app::main", "app::main")], + files: &[("app.rs", "fn main() {}\n")], + }; + let fx = Fixture::new("closed-consumers", &[lib_crate(), app]); + let g = fx.open(); + let base = Scope { + consumers: Consumers::AllInStore, + ..fx.scope(&["lib", "app"]) + }; + // Silence twin: every stored crate fully covered. + assert!(g + .impact_report("lib::entry", &base) + .unwrap() + .closed_in_scope()); + let excluded = Scope { + excluded: vec![("app".into(), vec!["feature plugins".into()])], + ..base + }; + let r = g.impact_report("lib::entry", &excluded).unwrap(); + assert_eq!( + r.gaps, + vec![Gap::RegionsExcluded { + krate: "app".into(), + regions: vec!["feature plugins".into()], + }] + ); + } + + /// Case 14: A missing symbol in a crate whose harvest is incomplete carries + /// `HarvestIncomplete`: the failed harvest may have dropped its definition. + #[test] + fn a_missing_symbol_carries_its_crate_harvest_gap() { + let fx = Fixture::new("missing-harvest", &[lib_crate()]); + let scope = fx.scope(&["lib"]); + assert!(fx + .open() + .impact_report("lib::nope", &scope) + .unwrap() + .gaps + .is_empty()); + std::fs::write( + fx.store.join("lib.residuals.tsv"), + format!("{RES_HEADER}BODY_NOT_STRUCTURED\tlib::nope\n"), + ) + .unwrap(); + let r = fx.open().impact_report("lib::nope", &scope).unwrap(); + assert!( + matches!(r.gaps.as_slice(), [Gap::HarvestIncomplete { krate, .. }] if krate == "lib"), + "{:?}", + r.gaps + ); + } +} + +/// Each declared fact removes exactly the gap it answers: every test shows the gap present +/// first, then absent once the fact is supplied. +mod narrowing { + use super::suite_b::{lib_crate, Fixture, Krate}; + use lance_graph_codegraph::impact_report::{Consumers, Gap, Scope, Targets}; + + #[test] + fn adding_a_declared_consumer_narrows_the_gap() { + let fx = Fixture::new("narrow_consumer", &[lib_crate()]); + let declared = |fx: &Fixture, crates: &[&str]| Scope { + consumers: Consumers::Declared(vec!["app".into()]), + ..fx.scope(crates) + }; + let r = fx + .open() + .impact_report("lib::entry", &declared(&fx, &["lib"])) + .unwrap(); + assert!( + r.gaps.contains(&Gap::ConsumersNotInStore { + crates: vec!["app".into()] + }), + "{:?}", + r.gaps + ); + + fx.add(&Krate { + name: "app", + rows: &[ + ("DEFINES", "app::main", "app::main"), + ("CALLS_RESOLVED", "app::main", "lib::entry"), + ], + files: &[("main.rs", "fn main() { lib::entry() }\n")], + }); + let r = fx + .open() + .impact_report("lib::entry", &declared(&fx, &["lib", "app"])) + .unwrap(); + assert!( + !r.gaps + .iter() + .any(|g| matches!(g, Gap::ConsumersNotInStore { .. })), + "{:?}", + r.gaps + ); + } + + #[test] + fn declaring_test_targets_narrows_the_gap() { + let fx = Fixture::new("narrow_targets", &[lib_crate()]); + let g = fx.open(); + let lib_only = Scope { + targets: vec![("lib".into(), Targets::LIB_ONLY)], + ..fx.scope(&["lib"]) + }; + let r = g.impact_report("lib::helper", &lib_only).unwrap(); + assert!( + r.gaps.iter().any(|x| matches!( + x, + Gap::TargetsNotExtracted { krate, missing } if krate == "lib" && missing.tests + )), + "{:?}", + r.gaps + ); + + let all = Scope { + targets: vec![("lib".into(), Targets::ALL)], + ..fx.scope(&["lib"]) + }; + let r = g.impact_report("lib::helper", &all).unwrap(); + assert!( + !r.gaps.iter().any(|x| matches!( + x, + Gap::TargetsNotExtracted { .. } | Gap::TargetsUndeclared { .. } + )), + "{:?}", + r.gaps + ); + } + + #[test] + fn removing_an_excluded_region_narrows_the_gap() { + let fx = Fixture::new("narrow_excluded", &[lib_crate()]); + let g = fx.open(); + let excluded = Scope { + excluded: vec![("lib".into(), vec!["feature iam".into()])], + ..fx.scope(&["lib"]) + }; + let r = g.impact_report("lib::helper", &excluded).unwrap(); + assert!( + r.gaps.contains(&Gap::RegionsExcluded { + krate: "lib".into(), + regions: vec!["feature iam".into()], + }), + "{:?}", + r.gaps + ); + + let none = Scope { + excluded: vec![], + ..fx.scope(&["lib"]) + }; + let r = g.impact_report("lib::helper", &none).unwrap(); + assert!( + !r.gaps + .iter() + .any(|x| matches!(x, Gap::RegionsExcluded { .. })), + "{:?}", + r.gaps + ); + } +}