fix(detect): prune htmlcov/, coverage.py's HTML report dir - #3437
fix(detect): prune htmlcov/, coverage.py's HTML report dir#3437jrkyvital wants to merge 1 commit into
Conversation
htmlcov/ is where `coverage html` writes its report. It is the Python counterpart of lcov-report/, which _SKIP_DIRS already prunes unconditionally, but htmlcov was absent from the set and the evidence gate on "coverage" (Graphify-Labs#2339) does not reach it: that gate only fires on a directory literally named "coverage". Modern coverage.py drops a `.gitignore` inside htmlcov/, which hides the tree from detect's ignore pass, but that cover is incidental. It is gone under --no-gitignore, and collect_files never consults it, so the extract path was pulling htmlcov/coverage_html_cb_*.js and htmlcov/status.json in as source: a minified bundle and a generated JSON, parsed and graphed. htmlcov needs no evidence gate. Unlike "coverage" it is not a plausible package name in any language, only a tool's default output directory, so it is added beside lcov-report rather than through a marker probe. Tests: three cases in tests/test_detect.py covering the detect walk, the collect_files path with the coverage.py .gitignore present, and the _is_noise_dir contract. All three fail on the unpatched tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TeARDWQjvnPZigoytREuHm
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
No blocking issues surfaced.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2553 functions depend on the 724 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 563 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
detect()— 109 callers, 15 callees - new:
_extract_generic()— 18 callers, 26 callees - new:
save_manifest()— 40 callers, 11 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - …and 43 more — each is listed as a finding
Verification — 2553 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: 1151 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
262 of 262 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— full-run-safetytests/test_affected_member_seed.py— full-run-safetytests/test_agents_platform.py— 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— impact, 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— impact, 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— impact, 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_carried_hyperedge_remap.py— full-run-safetytests/test_case_sensitive_resolution.py— full-run-safetytests/test_charmap_encoding.py— impact, full-run-safetytests/test_chunking.py— impact, full-run-safetytests/test_cjs_module_extension.py— impact, full-run-safetytests/test_claude_cli_backend.py— impact, 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— 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— impact, 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_member_calls.py— full-run-safetytests/test_cross_repo_shared_types.py— full-run-safetytests/test_csharp_call_site_generic_args.py— full-run-safetytests/test_csharp_enum_members.py— full-run-safetytests/test_csharp_field_generic_args.py— full-run-safetytests/test_csharp_generic_callsites.py— full-run-safety- … and 212 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.
· 51 more finding(s) on lines outside this diff (see the check run).
The problem
htmlcov/is wherecoverage htmlwrites its report. It is the Python counterpart oflcov-report/, which_SKIP_DIRSalready prunes unconditionally, buthtmlcovwas not in the set.The
coverageevidence gate (#2339) does not cover it: that gate fires only on a directory literally namedcoverage, and_has_coverage_artifactsis never consulted forhtmlcov.Modern coverage.py drops a
.gitignore(# Created by coverage.py/*) insidehtmlcov/, which hides the tree fromdetect's ignore pass — but that cover is incidental, not a decision:--no-gitignore;collect_filesnever consults it, so the extract path walked in.Measured on a fresh
coverage run+coverage htmltree atv8HEAD (0.9.57):A minified Istanbul-style JS bundle and a generated JSON, parsed and graphed as source. On a real repo the same shape shows up in
detectunder--no-gitignoreas a pile of generated.htmlfiles reported as documents and queued for semantic extraction — LLM tokens spent extracting concepts out of a coverage report, and a "pending extraction" backlog number that stops meaning anything.The change
One entry, beside
lcov-report:Unconditional, deliberately. The collision question that gated
coverage(#2339),env(#2058),snapshots(#1666) and is being asked ofout(#3347/#3424) does not arise here:htmlcovis not a plausible package or namespace name in any language — no PyPI package claims the name (404), it is not a directory convention anywhere, it is only coverage.py's default output dir. Same contract aslcov-report, which the comment right above it already states no package is ever named.If a maintainer would still rather have it gated,
_has_coverage_artifactsworks on it unchanged (coverage.py writes bothindex.htmlandstatus.jsonintohtmlcov/) — say the word and I will move it behind the gate.Tests
Three cases in
tests/test_detect.py, next to the existingcoverageones. All three fail on the unpatched tree:test_detect_skips_htmlcov_dir— the walk drops the tree and records it inpruned_noise_dirsrather than leaving it to the ignore pass;test_collect_files_skips_htmlcov_dir— the extract path, with the coverage.py.gitignorepresent, so it pins the actual leak rather than the incidentally-covered case;test_is_noise_dir_htmlcov_is_unconditional— the contract, with and without a parent.Suite:
5495 passed, 14 skipped, 3 failed. The 3 failures are pre-existing and environmental —test_ollama.py::test_detect_backend_ollama,test_ollama.py::test_detect_backend_none_without_envvars,test_extract_code_only_cli.py::test_mixed_repo_without_key_errors_and_points_at_code_only— all three fail identically on cleanv8on this machine (a local Ollama is up and a provider key is in the env). The fivetools.skillgenCI checks pass.Prior art / not a duplicate
No open issue or PR mentions
htmlcov. Related but distinct: #870 (addedlcov-report), #2339 (gated barecoverage), #2479 / #3347 / #3424 (the ambiguous-name gating question forbuild/out) — this one is the unambiguous-name case those PRs explicitly leave alone.🤖 Generated with Claude Code
https://claude.ai/code/session_01TeARDWQjvnPZigoytREuHm