Skip to content

fix(detect): prune htmlcov/, coverage.py's HTML report dir - #3437

Open
jrkyvital wants to merge 1 commit into
Graphify-Labs:v8from
jrkyvital:fix/skip-htmlcov-dir
Open

fix(detect): prune htmlcov/, coverage.py's HTML report dir#3437
jrkyvital wants to merge 1 commit into
Graphify-Labs:v8from
jrkyvital:fix/skip-htmlcov-dir

Conversation

@jrkyvital

Copy link
Copy Markdown

The problem

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 not in the set.

The coverage evidence gate (#2339) does not cover it: that gate fires only on a directory literally named coverage, and _has_coverage_artifacts is never consulted for htmlcov.

Modern coverage.py drops a .gitignore (# Created by coverage.py / *) inside htmlcov/, which hides the tree from detect's ignore pass — but that cover is incidental, not a decision:

  • it is gone under --no-gitignore;
  • collect_files never consults it, so the extract path walked in.

Measured on a fresh coverage run + coverage html tree at v8 HEAD (0.9.57):

>>> collect_files(root)   # before
['app.py', 'htmlcov/coverage_html_cb_dd2e7eb5.js', 'htmlcov/status.json', 'test_app.py']
>>> collect_files(root)   # after
['app.py', 'test_app.py']

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 detect under --no-gitignore as a pile of generated .html files 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:

    "lcov-report",                          # Vitest/Istanbul/nyc HTML reports (#870);
                                            # bare "coverage" is gated on report
                                            # artefacts below (#2339)
    "htmlcov",                              # coverage.py's `coverage html` output dir

Unconditional, deliberately. The collision question that gated coverage (#2339), env (#2058), snapshots (#1666) and is being asked of out (#3347/#3424) does not arise here: htmlcov is 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 as lcov-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_artifacts works on it unchanged (coverage.py writes both index.html and status.json into htmlcov/) — say the word and I will move it behind the gate.

Tests

Three cases in tests/test_detect.py, next to the existing coverage ones. All three fail on the unpatched tree:

  • test_detect_skips_htmlcov_dir — the walk drops the tree and records it in pruned_noise_dirs rather than leaving it to the ignore pass;
  • test_collect_files_skips_htmlcov_dir — the extract path, with the coverage.py .gitignore present, 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 clean v8 on this machine (a local Ollama is up and a provider key is in the env). The five tools.skillgen CI checks pass.

Prior art / not a duplicate

No open issue or PR mentions htmlcov. Related but distinct: #870 (added lcov-report), #2339 (gated bare coverage), #2479 / #3347 / #3424 (the ambiguous-name gating question for build/out) — this one is the unambiguous-name case those PRs explicitly leave alone.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TeARDWQjvnPZigoytREuHm

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

@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.

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-safety
  • tests/test_affected_member_seed.py — full-run-safety
  • tests/test_agents_platform.py — 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 — impact, 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 — impact, 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 — impact, 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_carried_hyperedge_remap.py — full-run-safety
  • tests/test_case_sensitive_resolution.py — full-run-safety
  • tests/test_charmap_encoding.py — impact, full-run-safety
  • tests/test_chunking.py — impact, full-run-safety
  • tests/test_cjs_module_extension.py — impact, full-run-safety
  • tests/test_claude_cli_backend.py — impact, 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 — 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 — impact, 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_member_calls.py — full-run-safety
  • tests/test_cross_repo_shared_types.py — full-run-safety
  • tests/test_csharp_call_site_generic_args.py — full-run-safety
  • tests/test_csharp_enum_members.py — full-run-safety
  • tests/test_csharp_field_generic_args.py — full-run-safety
  • tests/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).

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.

1 participant