Skip to content

fix(cli): degrade instead of aborting extraction when --cargo finds no manifest - #3739

Closed
ayushcodes10 wants to merge 3 commits into
Graphify-Labs:v8from
ayushcodes10:fix-3677-cargo-missing-manifest-aborts-extraction
Closed

ayushcodes10 wants to merge 3 commits into
Graphify-Labs:v8from
ayushcodes10:fix-3677-cargo-missing-manifest-aborts-extraction

Conversation

@ayushcodes10

Copy link
Copy Markdown
Contributor

Fixes #3677.

graphify extract --cargo exits 1 when Cargo.toml is absent at the scan root — after the AST pass has already completed. No graph.json is written, so every file of structural extraction work is discarded because an optional enrichment pass found nothing to read.

The cause: FileNotFoundError is a subclass of OSError, so it's caught by except (ConnectionError, ImportError, OSError) as exc: sys.exit(1) — a handler meant for a missing tomli import or a genuine connection failure, not for an ordinary missing-manifest case (e.g. a Tauri app keeps its manifest in a separate subdirectory from the scan root).

The surrounding code already supports the graceful path: cargo_result is initialized to {"nodes": [], "edges": []} and merged additively downstream, so an empty cargo result is already a valid, handled outcome. This catches FileNotFoundError separately, prints a note, and lets extraction continue with the empty result instead of exiting.

Two regression tests in tests/test_cargo_missing_manifest.py: one confirms a repo with no Cargo.toml still writes the graph built from the code already indexed; a companion confirms a repo with a real manifest still gets its crate node, so the fix only changes the missing-manifest path. Both checked against pre-fix code via git stash first. Full suite (5716 passed) is green.

🤖 Generated with Claude Code

https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh

ayushcodes10 and others added 3 commits September 22, 2026 13:57
FileNotFoundError is a subclass of OSError, so a missing Cargo.toml at
the scan root was caught by the same handler meant for ImportError
and connection failures and exited the whole process. That fires
after the AST pass has already completed, discarding every file of
completed structural extraction work just because an optional
enrichment source found nothing to read, an ordinary condition for
any repo whose manifest lives in a subdirectory, such as a Tauri app
that keeps it under its own separate frontend adjacent crate folder.

cargo_result already starts as the empty, handled shape the merge
step downstream expects, so catch FileNotFoundError on its own, print
a note, and let extraction continue with an empty cargo result instead
of exiting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
One test runs extract with the cargo flag against a repo that has no
Cargo.toml at all and confirms it still succeeds and still writes the
graph built from the code already indexed, with a note explaining the
crate edges were skipped. A companion test confirms a repo that does
have a valid manifest still gets its crate node, so the fix only
changes the missing manifest path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh

@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 1 advisory finding(s) below merit a look before merge.


Graphify review — findings

Handles a missing root Cargo.toml in dispatch_command as an ordinary condition rather than a fatal error: graphify extract --cargo now catches the FileNotFoundError (previously swallowed by the OSError handler that exits), prints a skip note, and continues with the empty cargo result so the completed AST pass still gets written to graph.json. Adds tests covering both the manifest-less skip path and the normal case where a real Cargo.toml contributes crate nodes.

Worth a look

  • Broad FileNotFoundError catch turns unrelated Cargo introspection failures into success — graphify/cli.py:4185 · 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 — 488 functions depend on the 269 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 125 callees
  • new: _stale_graph_sources() — 7 callers, 6 callees
  • new: _run_hook_guard() — 4 callers, 8 callees
  • new: test_poisoned_manifest_is_healed() — 0 callers, 6 callees

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

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: 426 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

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

non-code file(s) changed (CHANGELOG.md) → running the full suite for safety (a code graph can't see config/fixture/data deps)

changed code file(s) with no mapped test (CHANGELOG.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.

· 4 more finding(s) on lines outside this diff (see the check run).

@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.66 (on PyPI). Cherry-picked with authorship preserved so it shows under your GitHub contributions. Thanks @ayushcodes10!

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.

--cargo aborts the entire extraction when no root Cargo.toml exists, discarding completed AST work

2 participants