Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/docproof/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@
superseded_lines,
suppressed_lines,
)
from .docs import by_directory, find_docs, read, unread_documents
from .docs import (
by_directory,
find_docs,
likeliest_docs_directory,
read,
unread_documents,
)
from .history import classify, vanished_documents
from .project import Project, find_root
from .report import Report
Expand Down Expand Up @@ -149,10 +155,21 @@ def report_coverage(project: Project, unread: list[Path]) -> None:
f"default scope is top-level files plus doc/ and docs/"
)
print(f" {where}")
print(
f" read them too with --docs '{groups[0][0]}/**/*.md' or "
f'[tool.docproof] docs = ["{groups[0][0]}/**/*.md"]'
)
# The directory to widen TO is not the biggest one. Measured over sweep batch 10:
# the largest unread segments across twenty-three repositories are `skills/`, `tools/`,
# `src/`, `crates/` and `.changeset/`, and one entry in the top nine is documentation.
# See `likeliest_docs_directory`.
widen = likeliest_docs_directory(groups)
if widen:
print(
f" read them too with --docs '{widen}/**/*.md' or "
f'[tool.docproof] docs = ["{widen}/**/*.md"]'
)
else:
print(
" none of them is named like a documentation tree; if one is, widen with "
"--docs 'DIR/**/*.md' or [tool.docproof] docs = [\"DIR/**/*.md\"]"
)


def report_set_aside(historical: list[str], disclaimed: dict[tuple[str, str], list[str]]) -> None:
Expand Down
36 changes: 36 additions & 0 deletions src/docproof/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,42 @@ def by_directory(root: Path, paths: Iterable[Path]) -> list[tuple[str, int]]:
return sorted(counts.items(), key=lambda item: (-item[1], item[0]))


# A directory whose NAME says it holds documentation, for the one line that tells the reader
# what to widen to. Everything here is a naming convention rather than a guess about contents.
DOCS_DIRECTORY = re.compile(
r"(?i)^(docs?([.\-_].*)?|.*\.wiki|.*wiki.*|handbooks?|guides?|manuals?|books?|"
r"readme_i18n|website|content|reference)$"
)


def likeliest_docs_directory(groups: list[tuple[str, int]]) -> str | None:
"""Which unread directory to SUGGEST widening to, given (name, count) biggest first.

**Measured across the twenty-three repositories of sweep batch 10, 2026-08-19.** They hold
4,819 unread documentation files, and ranking the suggestion by count points at the wrong
directory most of the time, because the biggest unread directory in a monorepo is almost
never its documentation. The top segments were `skills/` 429, `tools/` 176,
`docs.feldera.com/` 131, `datafusion/` 123, `.changeset/` 108, `.claude/` 108, `src/` 97,
`backends/` 77, `crates/` 69.

One of those nine is documentation. The rest are source-tree READMEs, agent skill
definitions and changelog fragments - which is to say `find_docs` is RIGHT to leave them
out, and its docstring already argues so. The defect was never the scope. It was that the
line telling a reader how to widen said `--docs 'ts/**/*.md'` at a project whose real
documentation site sat two entries below.

Returns None when nothing in the unread tree is named like documentation, and the caller
says so rather than naming the biggest. Suggesting `--docs 'apps/**/*.md'` at a monorepo,
which is what ranking by count did to `superset-sh/superset`, tells the reader to widen
into exactly the package-internal READMEs `find_docs` excludes on purpose. Advice that
produces findings nobody wants is worse than no advice.
"""
for name, _ in groups:
if DOCS_DIRECTORY.match(name):
return name
return None


def read(path: Path) -> str:
"""UTF-8 with universal newlines, so a CRLF checkout reads the same as a LF one."""
return path.read_text(encoding="utf-8", errors="replace").replace("\r\n", "\n")
Expand Down
41 changes: 41 additions & 0 deletions tests/test_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,44 @@ def test_full_coverage_adds_no_sentence(make_repo: Callable[..., Path], capsys)
main([str(repo)])
out = capsys.readouterr().out
assert "were never read" not in out


def test_the_widen_suggestion_names_the_documentation_tree(make_repo: Callable[..., Path], capsys) -> None:
"""**Measured over sweep batch 10, twenty-three repositories, 4,819 unread files.** The
biggest unread directory in a real project is almost never its documentation: the top
segments were `skills/` 429, `tools/` 176, `docs.feldera.com/` 131, `datafusion/` 123,
`.changeset/` 108, `.claude/` 108, `src/` 97. One of the top nine is documentation.

So ranking the suggestion by count told `immich` to read `mobile/`, `executorch` to read
`examples/` and `deepagents` to read `libs/`, while each of them keeps a wiki or an i18n
README tree the line never mentioned.
"""
files = {"README.md": README, "src/thing.py": "x = 1\n", "project.wiki/page.md": "# W\n"}
for n in range(6):
files[f"crates/pkg{n}/README.md"] = "# Crate\n"
repo = make_repo(files)
main([str(repo)])
out = capsys.readouterr().out
assert "crates/ 6" in out, "the LIST still ranks by count, which shows the shape"
assert "--docs 'project.wiki/**/*.md'" in out
assert "--docs 'crates/**/*.md'" not in out


def test_no_documentation_tree_means_no_confident_suggestion(make_repo: Callable[..., Path], capsys) -> None:
"""`superset-sh/superset` has 276 unread files and not one directory named like
documentation - `apps/ 116`, `plans/ 100`, `packages/ 21`. Naming the biggest would tell
the reader to widen into exactly the package-internal READMEs `find_docs` excludes on
purpose, so the advice would manufacture the findings the scope exists to avoid.

Both widening routes are still printed, because a reader whose documentation genuinely
lives in an oddly named directory still needs to know how to say so.
"""
files = {"README.md": README, "src/thing.py": "x = 1\n"}
for n in range(4):
files[f"apps/app{n}/README.md"] = "# App\n"
repo = make_repo(files)
main([str(repo)])
out = capsys.readouterr().out
assert "none of them is named like a documentation tree" in out
assert "--docs 'apps/**/*.md'" not in out
assert "--docs" in out and "[tool.docproof] docs" in out
Loading