Tooling · Documentation
A Path-Existence Checker
Found a Real Bug on Day One
A heuristic script that does nothing but compare backtick-quoted paths in the docs against the real file tree — no parsing, no understanding of what the code inside a snippet actually does — caught a real bug in four Kotlin docs on its first run. The design decisions that kept it from crying wolf ended up mattering more than the check itself.
A round of closing doc/code gap issues had just wrapped up, and the next question was where to spend the next block of time: build an automatic doc-drift detector, run a real benchmark, build a third domain, or expand the write-ups. The drift detector won, for a specific reason — every one of those gap issues had been found the same way, by a human or an agent reading a doc and separately reading the code it described and noticing they'd stopped agreeing. That's a repeatable pattern, and repeatable patterns are worth automating even in a cheap, dumb form.
Cheap and Dumb, on Purpose
scripts/check_docs_drift.py checks exactly two things, both pure string matching against the real file tree, with zero understanding of what any code actually does:
- STALE-ABSENCE — the doc says something doesn't exist yet, and it actually does.
- PHANTOM-PRESENCE — the doc labels a snippet "actual code" and names a real-looking path, and no such file exists.
That's the entire detection surface. It doesn't parse the snippet, doesn't diff it against the named file, and has no opinion on whether the code shown is what the file actually contains — only on whether the path named next to it exists. The honest way to describe it is a path-existence checker wearing a code-review costume.
The Interesting Part Was All in the Exceptions
A checker this literal is mostly a false-positive generator until it's taught what not to flag, and each exclusion came from testing against the docs and finding a specific way the naive version was wrong:
- A code-block header that says "to add," "proposed," or "target shape" is never read as STALE-ABSENCE — in this repository that phrasing overwhelmingly means "add this to a file that already exists," and testing against files that are always present (
build.gradle,main.go,application.yml) showed reading it as "doesn't exist" was wrong every single time. - A qualified reference like
pkg.path.TypeNameis recognized by capitalization — if the segment after the last dot starts uppercase, it's a type reference, not a file path, and gets skipped. - Any path or header containing
...is skipped outright — an elision, not a real path. .mdcross-references between docs are excluded from the existence check entirely, since a doc-to-doc link is essentially always present and checking it proves nothing.
None of these are clever. All of them came from a real false positive first, then got written down as a rule.
What It Found on Day One
Wired into CI to run on every push touching **/*.md or implementations/*/examples/**, the first real run caught something worth catching: four Kotlin docs — config.md, module-pattern.md, observability.md, secret-manager.md — each cited a code block's header as notification/infrastructure/X.kt. The real path was account/infrastructure/notification/X.kt — the domain prefix was missing and the two segments were in the wrong order. Four docs, the same wrong path, fixed in the same commit that shipped the checker.
Prose that describes a gap without a backtick-quoted path — "the app service isn't in compose" written as a sentence, no path — is invisible to it. So is everything inside a snippet: whether the code shown still matches what the named file actually contains is a question this tool has no way to ask. It knows one thing — does the path exist — and answers only that.
Why the Cheap Version Still Earns Its Keep
Most doc drift in a project like this isn't "the logic subtly changed and the doc's explanation is now wrong" — it's "the file moved, or was renamed, and the one line naming it in a doc never got updated." That's a mechanical mistake, and a mechanical check catches it without needing to understand a single line of the code it's checking. The engineering effort here went almost entirely into the four exclusion rules, not the two detection rules — teaching a literal-minded script what to ignore turned out to be the part that decided whether anyone would trust its output.
scripts/check_docs_drift.py — the full checker, under 250 lines · docs/docs-drift-check.md — what it checks and what it deliberately doesn't