Skip to content

Make the file counts in CLAUDE.md and README.md checkable - #297

Merged
piyushmakhija28 merged 1 commit into
mainfrom
fix/enforce-doc-counts
Aug 7, 2026
Merged

piyushmakhija28 merged 1 commit into
mainfrom
fix/enforce-doc-counts

Conversation

@piyushmakhija28

Copy link
Copy Markdown
Collaborator

Closes item 1 of #296.

Seven of the nine file counts CLAUDE.md and README.md advertise were wrong, two by more than a factor of two:

Claim Said Actual
langgraph_engine/*.py 244 253
repo-wide *.py 395 493
test_*.py 45 100
unit tests 36 91
integration tests 4 5
docs/ files 156 255
e2e / load 3 / 1 3 / 1

README.md separately claimed 44 for the same figure CLAUDE.md put at 45, in two different places. Two documents drifting to different wrong values for one number is only possible when neither is checked against anything, and that — not the arithmetic — is what this PR is about.

The approach is borrowed, not invented

docs/README.md carries the same kind of counts and they are correct, because tests/test_docs_index_matches_the_tree.py compares them against a live enumeration of the tree. This applies that technique to the remaining two files so the drift cannot recur, rather than fixing it once and waiting for it to happen again.

Counts come from git ls-files, not the filesystem, for the reason that test records in its own docstring: the documentation describes the repository, not whoever's working tree is on disk. Its first version counted os.listdir and passed locally while failing in CI, because a gitignored analysis dump existed on one machine and in no clone.

What is deliberately not checked

CLAUDE.md's 578 classes, 3,985 methods call-graph figures would need a full AST build per test run and would churn on any code change. Its 13 servers MCP count describes repositories that cannot be enumerated from here. A check that is expensive, or that cannot see its subject, teaches people to ignore it — so neither is asserted.

Verified rather than assumed

The test was written first and watched fail on all fourteen claims before a single number was corrected, so it is known to read the real files rather than pass vacuously. test_every_claim_pattern_matches_its_file guards that property permanently — a regex that stops matching its file is otherwise a silent pass, which is exactly the failure mode being fixed.

Its own guard caught a bug in its own counters. _test_modules_in added one to the prefix depth, so "unit tests" reported the nine files one level deeper (tests/*/test_*.py) and the three per-directory counters reported zero. test_no_counter_returns_zero failed and named them. The arithmetic guard alone would not have caught it — 9 + 0 + 0 + 0 still equals 9 — which is why both guards exist.

Adding this file changes the numbers it checks. It is a tracked test_*.py, so the totals it must assert are the post-commit ones (100 / 91 / 121 / 493), not the ones measured before staging it. Confirmed by staging the file and recomputing from the index rather than by predicting the increment — a test that went red on its own merge commit would have been a poor advertisement for the idea.

Local: the new file and test_docs_index_matches_the_tree.py pass together (13 tests), every test that reads CLAUDE.md or README.md passes, verify_open_encoding and verify_home_paths pass, collection is clean, and git diff --numstat shows 2/2 and 8/8 rather than whole-file line-ending flips.

CI is the authority on Linux and on py3.10/3.11 — the last PR proved that distinction the hard way.

🤖 Generated with Claude Code

Seven of the nine counts these two files advertise were wrong, two of them by
more than a factor of two:

    langgraph_engine/*.py    244 -> 253
    repo-wide *.py           395 -> 493
    test_*.py                 45 -> 100   (README said 44 in two other places)
    unit tests                36 ->  91
    integration tests          4 ->   5
    docs/ files              156 -> 255
    e2e / load                 3 / 1      (correct by coincidence, not by check)

Correcting them is the smaller half. They went stale because nothing could tell
that they had, and README and CLAUDE.md had drifted to different wrong values
for the same figure -- 44 against 45 -- which is only possible when neither is
checked against anything.

docs/README.md carries the same kind of per-folder counts and they are right,
because test_docs_index_matches_the_tree.py compares them to a live enumeration.
This applies that technique to the remaining two files, so the drift cannot
recur rather than merely being fixed once.

Counts come from `git ls-files`, not the filesystem, for the reason the docs
index test records in its own docstring: the documentation describes the
repository, not whoever's working tree is on disk. A gitignored scratch file
would otherwise make every claim wrong in every clone but the author's.

Deliberately unchecked: CLAUDE.md's "578 classes, 3,985 methods" call-graph
figures, which would need a full AST build per test run and would churn on any
code change, and its "13 servers" MCP count, which describes repositories that
cannot be enumerated from here. A check that is expensive, or that cannot see
its subject, teaches people to ignore it.

Three things this was verified against rather than assumed:

The test was written first and watched fail on all fourteen claims before any
number was corrected, so it is known to read the real files rather than pass
vacuously. test_every_claim_pattern_matches_its_file guards the same property
permanently -- a regex that stops matching its file is a silent pass otherwise.

test_no_counter_returns_zero caught a bug in this file's own counters:
_test_modules_in added one to the prefix depth, so "unit tests" reported the
nine files one level deeper and the three per-directory counters reported zero.
The arithmetic guard alone would not have caught it -- 9 + 0 + 0 + 0 still equals
9 -- which is why both guards exist.

Adding this file changes the numbers it checks. It is a tracked test_*.py, so
the totals it must assert are the post-commit ones (100/91/121/493), not the
ones measured before staging it. Verified by staging the file and recomputing
from the index rather than by predicting the increment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@piyushmakhija28
piyushmakhija28 merged commit 1afcecf into main Aug 7, 2026
6 checks passed
@piyushmakhija28
piyushmakhija28 deleted the fix/enforce-doc-counts branch August 7, 2026 13:42
piyushmakhija28 added a commit that referenced this pull request Aug 7, 2026
…ections

langgraph_engine/helper_nodes/ held five modules and 538 lines that nothing
imported. CLAUDE.md has described it as "scheduled for removal in a follow-up
cleanup" since the subpackage migration; this is that cleanup. Closes item 3 of
issue #296.

"No importers" was not the whole justification, and checking further changed
what this commit does. All nine of the package's public node functions are also
defined in orchestrator.py -- the module create_flow_graph actually wires -- so
this was an extraction that was begun and never finished, not a component that
lost its callers. Both copies were last touched by the same commit (06b0463,
the Level/Step rename), which had been maintaining them in lockstep.

Comparing the two copies function by function found five character-identical and
four divergent. The divergences are mostly the adaptations a deeper package
needs -- parent.parent.parent instead of parent.parent, renamed module constants,
different log tags -- but two of them are documentation corrections that exist
ONLY in the orphaned copy, with the stale text in the live one:

  save_workflow_memory's comment named ~/.claude/memory/sessions/ while the code
  beneath it writes to memory/logs/sessions/. The comment contradicted the line
  it described.

  optimize_context_after_level1 said "Level 3" in a docstring line, a summary
  line and an inline comment. Level 3 stopped existing in 06b0463, which renamed
  it to Level 2. The orphan's docstring was updated by that rename; the live
  one's was not.

Both are ported into orchestrator.py here, before the deletion rather than
alongside it, so nothing is discarded that the live module lacked. Deleting the
package first and noticing afterwards is how a fix disappears without anyone
recording that it ever existed.

The Quick Info counts move 253 -> 248 and 493 -> 488. That was not spotted by
hand: tests/test_doc_counts_match_the_tree.py, merged one commit ago in #297,
failed on the deletion and named both figures. It is the first thing that
enforcement caught, and it caught it immediately.

Verified: orchestrator imports and create_flow_graph(hook_mode=True) builds a
graph, every test that touches the orchestrator passes, all six CI gates pass,
test collection is clean, and no .py file anywhere still names helper_nodes.

CHANGELOG.md, docs/orchestration_prompt.md and docs/architecture/ still refer to
the package. The CHANGELOG entry and the architecture draft are point-in-time
records; orchestration_prompt.md explicitly sequenced this removal as separate
work, which is exactly what happened. Rewriting any of them would falsify the
history rather than correct it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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