Skip to content

v1.108.270 - A directory that declares itself a cache is not corpus

Choose a tag to compare

@jgravelle jgravelle released this 10 Aug 03:19
· 344 commits to main since this release

jCodeMunch now honours the Cache Directory Tagging Specification: a directory containing a CACHEDIR.TAG whose first 43 bytes are Signature: 8a477f597d28d172789f06886806bc55 is pruned from the walk, along with everything beneath it.

Why this exists

A sibling tool wrote a derived projection into a directory inside an indexed tree. jCodeMunch walked in and indexed its JSON as source, so content that was never project source came back from search_symbols and search_text. That tool then adopted CACHEDIR.TAG to declare the directory derived, and we ignored the declaration, because we had no notion of one. The containment it built did nothing for us.

Why a tag rather than another denylist entry

Three fixes were available and two are traps.

  • _SKIP_DIRECTORY_NAMES already lists .git, .venv, .tox — every dotted directory somebody thought of in advance. Adding the offender re-arms the same trap for the next tool.
  • A dotted-directory rule is better, but keys on a naming convention and cannot see a cache that is not dotted.
  • The tag is a declaration by the writer. It is the only one of the three that does not require every reader to know about every writer in advance.

The signature is verified

A file merely named CACHEDIR.TAG excludes nothing. A name-only check asserts one instance of the property instead of the property. Five parametrized lookalikes — empty, wrong hash, signature not first, truncated by one byte, wrong case — are controls, and a name-only implementation fails all five.

Scope

  • cache_dir is an ordinary exclusion, not a withheld reason. A tagged directory holds regenerable derived data by its writer's own declaration, which puts it in the same class as gitignore: the corpus being defined, not a file we refused. Coverage stays complete and absence claims over the remainder stay citable.
  • Reaches the full walk and the watcher fast path. resolve_explicit_paths deliberately bypasses it, with a test saying so: that route already opts past gitignore and skip-directory rules so a caller can name a generated file on purpose.
  • Local walks only. index_repo is deliberately uncovered, because validating the signature needs the blob's content and the GitHub tree listing carries only paths and sizes. A filename-only check is the one thing this release rejects, so the GitHub walk gets nothing rather than a lookalike. The gap is test-pinned.

Config key respect_cachedir_tag (default true; only an explicit false disables it) / JCODEMUNCH_RESPECT_CACHEDIR_TAG. Pruned directories are counted as cache_dir in discovery_skip_counts.

Verification

  • tests/test_v1_108_270.py, 31 tests. Non-vacuous against unmodified HEAD in an isolated worktree: 27 fail before the fix, and the 4 that pass on both sides are the controls.
  • Full suite 7501 passed, 7 skipped, 0 failed; clean-environment Python 3.13 run 7495 passed, 13 skipped, 0 failed. Both reconcile at 7508, and the delta from 1.108.269 is exactly the 31 new tests.
  • uv run ruff check src/ clean, and all 9 CI jobs green on the released SHA.