A File Git Had Not Met Yet

Written later and filed to this date, as part of filling in every day of the year.

The scanner that keeps private names off this site asks git for the list of tracked files and reads every one of them against the never-public patterns. That is the right surface for a repository: what git tracks is what a push would carry. It is the wrong surface for an inbox. A draft from the loop’s publish command lands as a new file under the content directory, and a new file is one git has not met. The tracked-surface scan does not see it. The suite stays green. Nothing has been checked, and the file is sitting exactly where a person would open it and start reading.

The lint that closes the gap went in on a Wednesday in June, the week a weekly draft cadence turned the inbox from an occasional paste into a standing intake. It is a short script with no dependencies beyond git itself. It asks git for the other list, the files under the content directory that are untracked and not ignored, adds every tracked piece whose frontmatter still says draft: true, and runs each line of each of them through the same pattern set the tracked-surface scan uses. The patterns were lifted out of that test into a shared module so the two could not drift apart, and the module lives under the tests directory on purpose: that path is excluded from the tracked scan, because a file that holds the patterns assembled would trip them.

Its commit message says what it is for: drafts must be screened before a human even reads them. Not before they are committed, before they are read. The order matters because the reader is the leak’s last chance to be noticed and the first place it can be copied somewhere else.

It did not catch everything, and the miss is the part worth keeping. In August an essay quoted the loopback hostname twice while describing which hosts a dev server admits without asking. It passed the draft lint, the whole suite and the guarded push, published to all three full-text feeds, and was caught by the post-deploy check that reads the live site. Two scanners, two pattern sets: the live check carried four rules about delivered surfaces that no pre-push gate used. Nothing was wrong with the lint’s idea. It was reading the right files against an incomplete list. In October the four rules moved into the shared module, and the lint, the source scan, the built-output scan and the live check now import one definition, so a rule added to any of them is a rule in all of them. The three fixes that followed, each widening the explicit-port rule for a case someone found, landed in that one file and took effect everywhere at once.

The draft lint, then: the only local gate that reads a file git does not track, because every other scanner reads git ls-files, and a draft is untracked by definition for exactly the hours in which someone will read it. It is the second of the four gates every publishing path runs, after the type check and before the suite, and it is the one that exists for a file nobody has yet agreed to keep.


← all writing