fix(cli): degrade instead of aborting extraction when --cargo finds no manifest - #3739
ayushcodes10 wants to merge 3 commits into
Conversation
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
There was a problem hiding this comment.
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-safetytests/test_affected_member_seed.py— full-run-safetytests/test_agents_platform.py— impact, full-run-safetytests/test_analyze.py— full-run-safetytests/test_anthropic_custom_endpoint.py— full-run-safetytests/test_antigravity_install.py— full-run-safetytests/test_apm_fallback_version.py— full-run-safetytests/test_architecture_doc.py— full-run-safetytests/test_astro_extraction.py— full-run-safetytests/test_astro_import_ids.py— full-run-safetytests/test_atomic_canvas_export.py— full-run-safetytests/test_atomic_version_stamp.py— full-run-safetytests/test_atomic_writes.py— full-run-safetytests/test_backend_env_isolation.py— full-run-safetytests/test_backend_extras.py— full-run-safetytests/test_benchmark.py— full-run-safetytests/test_benchmark_raw_graph.py— full-run-safetytests/test_build.py— full-run-safetytests/test_build_merge_dedup_scope.py— full-run-safetytests/test_build_merge_hyperedges_and_prune.py— full-run-safetytests/test_build_merge_shrink_guard.py— full-run-safetytests/test_builtin_global_type_refs.py— full-run-safetytests/test_cache.py— full-run-safetytests/test_callflow_html.py— full-run-safetytests/test_cargo_introspect.py— full-run-safetytests/test_cargo_missing_manifest.py— impact, changed-test, full-run-safetytests/test_carried_hyperedge_remap.py— full-run-safetytests/test_case_sensitive_resolution.py— full-run-safetytests/test_charmap_encoding.py— full-run-safetytests/test_chunking.py— full-run-safetytests/test_cjs_module_extension.py— full-run-safetytests/test_claude_cli_backend.py— full-run-safetytests/test_claude_md.py— full-run-safetytests/test_cli_broken_pipe.py— full-run-safetytests/test_cli_export.py— full-run-safetytests/test_cli_help.py— full-run-safetytests/test_cluster.py— full-run-safetytests/test_codebuddy.py— impact, full-run-safetytests/test_community_hub_labels.py— full-run-safetytests/test_community_labels_skill.py— full-run-safetytests/test_confidence.py— full-run-safetytests/test_corrupt_graph_json.py— full-run-safetytests/test_cpp_nested_and_cli.py— full-run-safetytests/test_cpp_objc_cross_file_calls.py— full-run-safetytests/test_cpp_preprocess.py— full-run-safetytests/test_cross_extension_reexport_self_cycle.py— full-run-safetytests/test_cross_language_call_resolution.py— full-run-safetytests/test_cross_repo_external_call_guards.py— full-run-safetytests/test_cross_repo_member_calls.py— full-run-safetytests/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).
|
Shipped in v0.9.66 (on PyPI). Cherry-picked with authorship preserved so it shows under your GitHub contributions. Thanks @ayushcodes10! |
Fixes #3677.
graphify extract --cargoexits 1 whenCargo.tomlis absent at the scan root — after the AST pass has already completed. Nograph.jsonis written, so every file of structural extraction work is discarded because an optional enrichment pass found nothing to read.The cause:
FileNotFoundErroris a subclass ofOSError, so it's caught byexcept (ConnectionError, ImportError, OSError) as exc: sys.exit(1)— a handler meant for a missingtomliimport 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_resultis initialized to{"nodes": [], "edges": []}and merged additively downstream, so an empty cargo result is already a valid, handled outcome. This catchesFileNotFoundErrorseparately, 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 noCargo.tomlstill 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 viagit stashfirst. Full suite (5716 passed) is green.🤖 Generated with Claude Code
https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh