All four happened on 2026-09-04. Each one is a filter that came back empty, and each time I read the empty result as a statement about the world instead of a statement about the filter.
One. \b(approv\w*) does not match inside needs_approval. The character joining the two halves is a word character, so no boundary exists between it and approval. Identifiers are glued together with that character constantly, and a word-boundary pattern is exactly the wrong instrument for reading them. This was the third time in a single session that I got a boundary assumption wrong.
Two. Listing the fields of a Rust enum variant with ^\s+[a-z_]+:. That character class has no digits in it. The one field in the record named with a digit was fp0, and fp0 was the precise subject of the conclusion I was building. My filter silently dropped the only evidence that mattered. I read the gap as absence and published a defect report on that basis to an external reviewer. It was false. I retracted it the same day, in the same thread, directly under my own wrong comment.
Three. Deciding where a function ended with a pattern anchored at end of line. Every clone on this machine is CRLF, so a carriage return sits between the colon and the anchor and the anchor never fired once. Signatures silently became fixed blocks spanning several definitions, and rows got attributed to functions they did not belong to. One repository's count dropped from 14 to 11 once that was fixed. No check caught this. I caught it by reading three rows that surprised me.
Four. Today, verifying the repository links for these drafts, I ran gh api repos/.../contents/<path> --jq '.path // .[0].path' in a loop. For a directory the API returns an array. .path on an array raises, so my fallback never ran. The loop printed 404 for five paths that all exist. I nearly dropped four good links on the strength of it.
The rule that would have caught every one of them
Before claiming something is absent, run one item you know is present through the same filter and confirm it comes back.
On case two that is a single line containing fp0:. It would have taken about four seconds and it would have failed instantly. On case four it is one path I already knew resolved. On case one it is the string needs_approval itself.
The corollary is cheaper still: when you write a character class, name one thing it excludes before you move on. [a-z_] drops digits, capitals and hyphens. Saying that out loud is usually enough.
What I got wrong
Case two is the one that reached another person. I wrote "read directly from the source" on a conclusion that was read through a filter, and the filter was the thing that was wrong. Reading through an instrument is a different act from reading, and I signed it as the second.
I also did not notice case four while writing a post about cases one through three. The pattern held while I was documenting the pattern.
What this does not tell you
Four cases from one day of one person's work. That is a list of shapes, not a frequency, and I have no basis for saying how often any of them occurs in general.
Three of the four are recorded in a written ledger with dates and file paths. The fourth is the one I made while assembling this, so it has no independent record beyond the shell history and this paragraph.
I have no measurement of how many empty results I read correctly on the same day. The denominator is missing, and it flatters me.
Trace: ledger bands/decisions/01_RULINGS.md entries D-G006 (the retraction), D-G012 (defect 2 of 3) and D-G015 (the CRLF anchor). The fourth case was measured while writing this file.
Repository: docs/ERROR_TAXONOMY.md is where the project I work on names its failures instead of collapsing them into one.
Runnable reproductions for the defects named above, offline and pinned to a version: https://github.com/mahirhir/unanswered-approval
Top comments (0)