find_dead_code's confidence: 1.0 is documented as provably unreachable. That is a claim about the tree, and it was computed from the index with nothing in between. Closes #566 and #569.
Runtime-discovered packages (#569)
encoding/schemas/ is enumerated by pkgutil.iter_modules(__path__) at import time — an edge no static import graph can see — so twelve live encoders were published at confidence 1.0. Which three of the fifteen escaped depended only on whether a test happened to import the module directly. search_symbols.py and search_text.py have the same role, the same shape and the same load path, and one was called dead. The signal was test-authoring habit.
A package that enumerates itself now makes its modules live roots. walk_packages extends that to subpackages; iter_modules deliberately does not. Where the enumerated directory cannot be resolved, the loader is reported and caps the confidence instead.
Corpus adequacy (#566)
A stale index and a too_large file that the size cap withheld each published live files as proven dead — the second because the withheld file's imports vanish with it. search_text handled the identical situation correctly on the identical index in the same session: absence_refused: true, complete: false, naming coverage.generation.git_head.
find_dead_code now reads those same disclosures and caps confidence at 0.6, below the 0.8 default, so the default call refuses rather than asserting. signal_warning names the cause; uncapped_confidence and confidence_capped_by ride on every clamped row.
unknown caps; not applicable does not. An index_repo snapshot has no local tree to compare against and is complete by construction, so it keeps its proof.
check_delete_safe, the destructive surface
Its "no refs at all" fallback reaches safe_to_delete regardless of the dead-code confidence it had just consulted, then floors that verdict at 0.85 — so capping the report alone still certified deletes over a corpus that could not support one. It now answers corpus_inadequate: classified, never terminal, with re-indexing named as the gap. Only absence verdicts are replaced; a found importer is positive evidence a thin corpus cannot unfind.
Also
get_dead_code_v2 shares the runtime roots — its signal 1 is the same import graph. Measured on this repo: files reported under src/ went 15 → 2, and those at confidence 1.0 went 12 → 0.
Suite 9135 passed / 13 skipped / 0 failed; ruff check src/ clean; CI-environment reproduce 9129 passed / 19 skipped, same 9148 total.