Skip to content

fix(elixir): resolve alias/import edges onto their defmodule node - #3566

Open
pavanetti wants to merge 1 commit into
Graphify-Labs:v8from
pavanetti:fix/elixir-alias-import-node-ids
Open

pavanetti wants to merge 1 commit into
Graphify-Labs:v8from
pavanetti:fix/elixir-alias-import-node-ids

Conversation

@pavanetti

Copy link
Copy Markdown

Fixes #3565.

Root cause

extract_elixir mints module definitions as _make_id(file_stem, module_name) — the corpus-wide invariant documented on _file_stem (#1504), which keeps same-named symbols in different files from collapsing — but emitted the reference side of alias/import/require/use as _make_id(module_name), with no stem. The two ids can never be equal, so build_from_json's src not in node_set or tgt not in node_set prune dropped every Elixir alias/import edge.

An Elixir alias names a BEAM module, not a path, so the per-file extractor cannot resolve it, and #2195's target_file probing does not apply. This is the same shape as Kotlin #2526 and is fixed the same way.

The fix

  • extractors/elixir.py stamps the written name as metadata.target_fqn, and marks top-level defmodule nodes with _elixir_module.
  • extract.py adds _resolve_elixir_import_targets, a corpus-wide pass mirroring _resolve_kotlin_import_targets: a name defined exactly once is repointed onto that node; zero or several candidates are left dangling, so no stub node is ever fabricated and confidence stays EXTRACTED (the label is the alias text as written, so the match is exact).

Definition ids are unchanged — existing graphs keep their node ids. This is why the resolver approach is preferable to making the definition id name-derived, which would churn every Elixir module id in every stored graph.

Measured

980-file Phoenix/Ecto app, lib/ only:

before after
imports emitted / surviving build 4,292 / 0 (0.0%) 4,292 / 3,531 (82.3%)
cross-file edge share 8.1% 27.4%
duplicate node ids 0 0
contains edges 1,012 1,012

Whole repo (2,470 .ex/.exs): 0 → 6,390 / 8,570 (74.6%). Unresolved remainder is genuine externals (Logger, Ecto.Query, deps).

Suite: 5,635 passed, 1 failed, 14 skipped. The failure is test_export.py::test_hyperedge_convex_hull_js_is_geometrically_sound, which fails identically on clean HEAD — pre-existing and environmental, not from this change.

Three guards, each with a regression test

Adversarial review found these; each is covered in tests/test_elixir_import_ids.py:

  1. Self-import must not destroy contains. The __using__/quote do import MyApp.Context end idiom makes a file alias a module it also defines. Retargeting lands the edge on the same (source, target) pair as file --contains--> module; the built graph is a plain non-multi Graph, so one relation silently overwrites the other. Measured at 17 lost contains edges before the guard. Self-imports carry no cross-file information and are now skipped.

  2. Nested defmodule must not capture a foreign alias. extract_elixir labels a nested module with the name as writtendefmodule Supervisor inside MyApp.Application is labelled Supervisor, not MyApp.Application.Supervisor. Indexing it would let it absorb use Supervisor (the stdlib module) from unrelated files and assert a false EXTRACTED edge. Only top-level defmodules are indexed, via _elixir_module. That marker also excludes any same-labelled node a semantic tier might add, which would otherwise make the name ambiguous and silently suppress resolution.

  3. Resolution must survive an incremental rebuild. The early resolver slot was handed all_nodes, which on a partial re-extract holds only the changed batch — so every alias into an unchanged file would dangle and be pruned, decaying the graph back toward the bug on exactly the graphify watch / hook path. Worse, it bypassed the single-candidate guard: a corpus-wide-ambiguous name can look unique within the changed subset and get an arbitrary target. Now indexes resolution_nodes, which includes the unchanged-corpus context (Incremental update loses cross-file edges from changed→unchanged files (batch-scoped import resolution + replace-on-re-extract) #2406) — matching that variable's own documented scope ("the member-call resolvers run by run_language_resolvers"). Inert for Kotlin, which indexes off per_file.

Risks / compatibility

  • _AST_CACHE_SCHEMA 2 → 3. The extractor's output changed (adds metadata.target_fqn), so a cache written by an earlier build would keep serving pre-fix results and make this change silently inert — verified: fix + pre-fix cache reproduces 0% resolution with no warning. The bump sweeps those entries. This also traps anyone re-measuring against a warm cache.
  • No node ids change, so stored graphs, manifests and incremental diffs stay valid. 0 duplicate ids on every corpus tested, including a purpose-built torture corpus (umbrella apps with same-named modules, .exs, defimpl, nested and reopened modules).
  • Ambiguous module names stay unresolved by design. A name defined in two files is left dangling rather than guessed.
  • graphify update --no-cluster writes the raw extraction, so its graph.json still carries some dangling imports (7 post-fix vs 16 pre-fix on the repro). Pre-existing behaviour, reduced but not eliminated.

Known limitation

This restores the module dependency layer only. extract_elixir emits no cross-module call edge in any style — neither Bar.run(x) after an alias nor fully-qualified MyApp.Foo.Bar.run(x) produces one — so the graph gains module-to-module imports, not a call graph. Noted in #3565.

Also noted in #3565: the same definition/reference id asymmetry reproduces with zero surviving cross-file edges in ~12 other extractors (Rust, Fortran, Julia, Apex, Verilog, Razor, Blade, Dart, Scala, Groovy, Zig, Pascal). Out of scope here; happy to follow up.

extract_elixir mints module definitions as _make_id(file_stem, name) but
emitted alias/import references as _make_id(name). The two can never be
equal, so build's node-set prune dropped every Elixir alias/import edge:
2199 emitted, 0 surviving on a 400-file sample.

An Elixir alias names a BEAM module, not a path, so the per-file extractor
cannot resolve it. Same shape as Kotlin Graphify-Labs#2526, fixed the same way: stamp the
written name as metadata.target_fqn and repoint the edge in a corpus-wide
resolver, guarded on a single candidate so nothing is fabricated.

Definition ids are unchanged, so existing graphs keep their node ids.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pavanetti
pavanetti changed the base branch from main to v8 September 15, 2026 02:01

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Formal verification. 2 change(s) alter behavior, breaking input(s) attached.

Behavior changes: \_\_getattr\_\_ changes behavior, here is the input that shows it.

The verifier found a concrete input on which \_\_getattr\_\_ behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"name":"'save\_query\_result'"\}, the old code produced raises AttributeError but the new code produces \<function save\_query\_result at 0x7f67338a8400\>. Paste that input straight into a regression test.

Behavior changes: run\_benchmark changes behavior, here is the input that shows it.

The verifier found a concrete input on which run\_benchmark behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph\_path":"None","corpus\_words":"None","questions":"None"\}, the old code produced raises TypeError but the new code produces \{'corpus\_tokens': 1099466, 'corpus\_words': 824600, 'nodes': 16492, 'edges': 31042, 'avg\_query\_tokens': 17027, 'reduction\_ratio': 64\.6, 'per\_question': \[\{'question': 'how does authentication…. Paste that input straight into a regression test.


Graphify review — findings

Adds full CI/CD and repo hygiene scaffolding: a CI workflow running skillgen drift checks, a 3.10–3.14 pytest matrix, end-to-end install verification, and non-blocking bandit/pip-audit scans; a trusted-publishing publish.yml gated on tag/version match; and a release-graph.yml that builds an AST-only self-graph asset per release. Adds .dockerignore, .gitattributes (marking generated HTML as vendored so Linguist reports Python), FUNDING config, a ruff + skillgen pre-commit config, AGENTS.md, and expands .gitignore while keeping generated graphify/skills/ and skillgen fragments tracked.

Worth a look

  • Unpinned PyPI publish actions run with OIDC publishing permission.github/workflows/publish.yml:31 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • HTTP server image defaults to no API keyDockerfile:25 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Release asset workflow uses unpinned actions with write token.github/workflows/release-graph.yml:12 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Build copies entire context into imageDockerfile:12 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • README gives conflicting default contract for query loggingREADME.md · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 16151 functions depend on the 16151 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 599 callers, 44 callees
  • new: _rebuild_code() — 116 callers, 51 callees
  • new: build_from_json() — 207 callers, 19 callees
  • new: deduplicate_entities() — 76 callers, 24 callees
  • new: detect() — 112 callers, 15 callees
  • new: build_merge() — 76 callers, 14 callees
  • new: to_obsidian() — 38 callers, 14 callees
  • new: save_semantic_cache() — 58 callers, 9 callees
  • …and 235 more — each is listed as a finding

Verification — 16151 functions in the blast radius were not formally verified this run (proofs are advisory here).

Health delta baseline: last indexed commit fe66389 (diverged from this PR's base — delta is approximate).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 16151 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

276 of 276 test file(s) selected (100%) via static blast radius.

Escalated to a full run for safety — the selection is not trustworthy on its own (see below). CI should run the whole suite.

  • tests/test_affected_cli.py — impact, changed-test, full-run-safety
  • tests/test_affected_member_seed.py — impact, changed-test, full-run-safety
  • tests/test_agents_platform.py — impact, changed-test, full-run-safety
  • tests/test_analyze.py — impact, changed-test, full-run-safety
  • tests/test_anthropic_custom_endpoint.py — changed-test, full-run-safety
  • tests/test_antigravity_install.py — changed-test, full-run-safety
  • tests/test_apm_fallback_version.py — impact, changed-test, full-run-safety
  • tests/test_architecture_doc.py — impact, changed-test, full-run-safety
  • tests/test_astro_extraction.py — impact, changed-test, full-run-safety
  • tests/test_astro_import_ids.py — impact, changed-test, full-run-safety
  • tests/test_atomic_canvas_export.py — impact, changed-test, full-run-safety
  • tests/test_atomic_version_stamp.py — impact, changed-test, full-run-safety
  • tests/test_atomic_writes.py — impact, changed-test, full-run-safety
  • tests/test_backend_env_isolation.py — impact, changed-test, full-run-safety
  • tests/test_backend_extras.py — impact, changed-test, full-run-safety
  • tests/test_benchmark.py — impact, changed-test, full-run-safety
  • tests/test_benchmark_raw_graph.py — impact, changed-test, full-run-safety
  • tests/test_build.py — impact, changed-test, full-run-safety
  • tests/test_build_merge_dedup_scope.py — impact, changed-test, full-run-safety
  • tests/test_build_merge_hyperedges_and_prune.py — impact, changed-test, full-run-safety
  • tests/test_build_merge_shrink_guard.py — impact, changed-test, full-run-safety
  • tests/test_builtin_global_type_refs.py — impact, changed-test, full-run-safety
  • tests/test_cache.py — impact, changed-test, full-run-safety
  • tests/test_callflow_html.py — impact, changed-test, full-run-safety
  • tests/test_cargo_introspect.py — impact, changed-test, full-run-safety
  • tests/test_carried_hyperedge_remap.py — impact, changed-test, full-run-safety
  • tests/test_case_sensitive_resolution.py — impact, changed-test, full-run-safety
  • tests/test_charmap_encoding.py — impact, changed-test, full-run-safety
  • tests/test_chunking.py — impact, changed-test, full-run-safety
  • tests/test_cjs_module_extension.py — impact, changed-test, full-run-safety
  • tests/test_claude_cli_backend.py — impact, changed-test, full-run-safety
  • tests/test_claude_md.py — impact, changed-test, full-run-safety
  • tests/test_cli_broken_pipe.py — changed-test, full-run-safety
  • tests/test_cli_export.py — impact, changed-test, full-run-safety
  • tests/test_cli_help.py — changed-test, full-run-safety
  • tests/test_cluster.py — impact, changed-test, full-run-safety
  • tests/test_codebuddy.py — impact, changed-test, full-run-safety
  • tests/test_community_hub_labels.py — impact, changed-test, full-run-safety
  • tests/test_community_labels_skill.py — impact, changed-test, full-run-safety
  • tests/test_confidence.py — impact, changed-test, full-run-safety
  • tests/test_corrupt_graph_json.py — impact, changed-test, full-run-safety
  • tests/test_cpp_nested_and_cli.py — impact, changed-test, full-run-safety
  • tests/test_cpp_objc_cross_file_calls.py — impact, changed-test, full-run-safety
  • tests/test_cpp_preprocess.py — impact, changed-test, full-run-safety
  • tests/test_cross_extension_reexport_self_cycle.py — impact, changed-test, full-run-safety
  • tests/test_cross_language_call_resolution.py — impact, changed-test, full-run-safety
  • tests/test_cross_repo_member_calls.py — impact, changed-test, full-run-safety
  • tests/test_cross_repo_shared_types.py — impact, changed-test, full-run-safety
  • tests/test_csharp_call_site_generic_args.py — impact, changed-test, full-run-safety
  • tests/test_csharp_enum_members.py — impact, changed-test, full-run-safety
  • … and 226 more

non-code file(s) changed (.dockerignore, .gitattributes, .github/FUNDING.yml, .github/workflows/ci.yml, .github/workflows/publish.yml …) → running the full suite for safety (a code graph can't see config/fixture/data deps)

changed code file(s) not in the graph (tests/eval_attention.py, tests/fixtures/sample.dm) — deleted or unindexed, so their dependent tests can't be found; running the full suite for safety

changed code file(s) with no mapped test (.github/workflows/ci.yml, .github/workflows/publish.yml, .github/workflows/release-graph.yml, AGENTS.md, ARCHITECTURE.md …) — a coverage gap or a missing link — running the full suite rather than only the selected tests

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Behavior changes: \_\_getattr\_\_ changes behavior, here is the input that shows it.

The verifier found a concrete input on which \_\_getattr\_\_ behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"name":"'save\_query\_result'"\}, the old code produced raises AttributeError but the new code produces \<function save\_query\_result at 0x7f67338a8400\>. Paste that input straight into a regression test.

Behavior changes: run\_benchmark changes behavior, here is the input that shows it.

The verifier found a concrete input on which run\_benchmark behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.

Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.

Evidence: On input \{"graph\_path":"None","corpus\_words":"None","questions":"None"\}, the old code produced raises TypeError but the new code produces \{'corpus\_tokens': 1099466, 'corpus\_words': 824600, 'nodes': 16492, 'edges': 31042, 'avg\_query\_tokens': 17027, 'reduction\_ratio': 64\.6, 'per\_question': \[\{'question': 'how does authentication…. Paste that input straight into a regression test.

No difference found (not proven): No behavior difference found in main (not a proof).

The verifier ran both versions of main on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_cross\_community\_surprises (not a proof).

The verifier ran both versions of \_cross\_community\_surprises on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_cross\_file\_surprises (not a proof).

The verifier ran both versions of \_cross\_file\_surprises on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

No difference found (not proven): No behavior difference found in \_file\_category (not a proof).

The verifier ran both versions of \_file\_category on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify \_is\_file\_node.

The verifier did not have enough to check \_is\_file\_node, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 1 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Could not verify: Could not verify \_surprise\_score.

The verifier did not have enough to check \_surprise\_score, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in god\_nodes (not a proof).

The verifier ran both versions of god\_nodes on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify graph\_diff.

The verifier did not have enough to check graph\_diff, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: the input domain has 25 values but only 5 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)

Could not verify: Could not verify suggest\_questions.

The verifier did not have enough to check suggest\_questions, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ValueError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).

The verifier ran both versions of \_query\_subgraph\_tokens on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify print\_benchmark.

The verifier did not have enough to check print\_benchmark, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly KeyError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build.

The verifier did not have enough to check build, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify build\_from\_json.

The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify cache\_dir.

The verifier did not have enough to check cache\_dir, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify cached\_files.

The verifier did not have enough to check cached\_files, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify check\_semantic\_cache.

The verifier did not have enough to check check\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify clear\_cache.

The verifier did not have enough to check clear\_cache, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_cached.

The verifier did not have enough to check save\_cached, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_semantic\_cache.

The verifier did not have enough to check save\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in \_split\_community (not a proof).

The verifier ran both versions of \_split\_community on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify cluster.

The verifier did not have enough to check cluster, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Elixir: every alias/import edge is dropped at build — definition ids carry a file-stem prefix, reference ids do not

1 participant