Skip to content

ci(docs): enumerate tracked files instead of walking the filesystem#260

Merged
jsugg merged 2 commits into
mainfrom
ci/hermetic-docs-gate
Jul 15, 2026
Merged

ci(docs): enumerate tracked files instead of walking the filesystem#260
jsugg merged 2 commits into
mainfrom
ci/hermetic-docs-gate

Conversation

@jsugg

@jsugg jsugg commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Problem

docs:validate walked the filesystem with a hardcoded skip list (.git,
coverage, node_modules, reports) and never consulted .gitignore. A
required check therefore returned a different answer depending on the
developer's disk:

docs:validate OK (12 Markdown files)   <- locally
docs:validate OK  (8 Markdown files)   <- CI, clean checkout

The extras were scratch notes under .local/, deliberately untracked.

Three consequences, in increasing order of how much they matter:

  1. The gate's result is not reproducible — it depends on untracked local state.
  2. It reads files that were deliberately kept out of version control.
  3. Its failure mode is inverted: a local note saved with CRLF or containing
    a conflict marker fails docs:validate on the author's machine while CI
    passes, because CI never had the file. The gate fails the one person who
    cannot see why, and passes for everyone who can.

Change

Enumerate with git ls-files -z -- '*.md'. --root support is kept for tests.

The local set and the clean-checkout set are now identical by construction,
rather than by the skip list happening to keep pace with the directory layout —
which it had already stopped doing.

Acceptance

With .local/ present on disk:

$ npm run docs:validate
docs:validate OK (8 Markdown files)

$ diff <(git ls-files '*.md' | sort) <(docs:validate --json | jq -r '.files[]' | sort)
  (identical)

Note on the fixtures

They are now real git repositories. Writing a file to disk no longer makes it
visible to the gate — which is exactly the property being fixed, so it is
asserted directly rather than assumed: untracked Markdown is ignored, and
violations inside an untracked local note no longer fail the run. That last test
is the local-vs-CI inversion above, pinned so it cannot come back.

The drift this exposed, for the record: the audit measured 11 files, and it was
12 by the time the fix landed — a new untracked note had appeared in between.
The moving number is the argument.

jsugg added 2 commits July 15, 2026 04:38
docs:validate walked the working tree with a hardcoded skip list and never
consulted .gitignore, so a required check reported a different verdict
depending on what happened to be lying around on the developer's disk:

  docs:validate OK (12 Markdown files)   <- locally
  docs:validate OK  (8 Markdown files)   <- CI, clean checkout

The four extra files were scratch notes under .local/, deliberately untracked.
That made the gate irreproducible, made it read files kept out of version
control on purpose, and inverted its failure mode: a local note saved with CRLF
or holding a conflict marker failed the gate on the author's machine while CI —
which never had the file — passed.

Ask git for the file set instead. The local result and the clean-checkout result
are now the same set by construction rather than by coincidence of the skip list
keeping pace with the directory layout.

The fixtures become real repositories, because writing a file to disk no longer
makes it visible to the gate. That is the property under test, so it is now
asserted directly: untracked Markdown is ignored, and violations inside an
untracked local note do not fail the run.
@jsugg
jsugg merged commit 1218817 into main Jul 15, 2026
21 checks passed
@jsugg
jsugg deleted the ci/hermetic-docs-gate branch July 15, 2026 07:46
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