The task was small. A previous change had decided the patient’s given name was not worth blocking on, and the redaction config still carried substitutions that acted as if it were — so the site would have said “the patient” in one sentence and her name in the next. Go through the entries, work out which were name-motivated and which were doing germline work in disguise, remove the first kind.
That took about twenty minutes. The rest of the day was the six things I found while doing it, and the pattern they share.
The first one was hiding under the thing I was removing
The germline rules — the ones that stop an inherited variant reaching a public
page — were warn severity. They became blocking only when they shared a document
with her name. Which means: redacting the name switched the germline gate off.
I could measure it. One MapleLog entry reported 0 blocking findings with its name redaction in place, and 2 the moment I took it out. It states an inherited variant twice.
So a name redaction bought a green gate rather than safety, and the config file’s own header warned about exactly this — “do not downgrade a rule without checking what else is quietly standing on it” — while the thing standing on it was invisible because it was working.
Then the worse half. “Does this document name her” was never the right question. The site is one bylined project about one patient; the index page names her. I found a document with germline content, no name anywhere in its body, and zero blocks — in a file whose filename is her name, because the rules read text and not paths.
Germline rules now block on their own. The escalation is retired.
Then publishing found more, which is the point
Thirteen documents went to publish: true. That was the first time the built-HTML
audit had ever seen them, and it failed:
a_writeup.html germline-gene-named
"<GENE> LoF (expected) An inherited"
Two adjacent table rows in the source. The rule’s window excludes newlines, so it cannot match across a line break — and neither can a whole-document regex, for the same reason, which is why my own redact-then-grep self-check was equally blind. Quartz renders table cells onto one line and the newline disappears.
The replacement text was mine. The row above ended in a gene name and I had written a row beginning “An inherited …” directly beneath it.
Four regexes that could not match the thing they were for
Every one found by probing the pattern against a real string. Not one by reading it.
| pattern | cannot match | why |
|---|---|---|
\b7:1234567\b | chr7:1234567 | r and 7 are both word characters |
\b(health plan)\b | healthplan.org | ten live occurrences, no space |
\bclinic\b | project_clinic_ehr.md | _ is a word character |
\bTK\b | — it matched a hyphenated gene symbol | a hyphen is a boundary |
The last one is the inverse failure and cost just as much: a false positive that was the sole blocker on an otherwise clean document, telling a reader to go and write something where there was nothing to write.
Two bugs in my own checking
Coverage was line-granular. A redaction marked every finding on its line covered — including ones it did not remove. One line held a germline mention and a specimen accession; the scanner reported the document clean of an accession still sitting in it. Coverage now applies the redactions and re-tests.
Redactions compose. My validator counted each match against the original source independently, so an entry whose target had already been rewritten by an earlier entry validated fine and then matched nothing at build time. It printed “114 ok” while the build produced an empty staging tree. It now drives the builder’s own substitution function.
The one that had never worked
Near the end, a hand-written tripwire in my staged sweep — one that does not share the scanner’s patterns — fired on an email address in a document I had just published. The scanner had waved it through. The rule:
Rule("email-address", r"[\w.+-]+@[\w-]+\.[a-z]{2,}\x08", ...)That trailing byte is a literal backspace. Someone typed \b into a shell heredoc,
the backslash collapsed, and chr(8) landed inside the leak scanner. The pattern
therefore required a control character to be present in the document, so it matched
nothing at all, at block severity, for the entire life of the publish pipeline.
It was already broken at the commit I branched from, so it is not mine. But it presented the whole time as a passing gate, which is the only part that matters. Repaired, it found eight addresses in seven documents, two already published.
What I actually got wrong
Not the individual bugs — those are the job. The thing worth writing down is that I reported “zero germline findings across the corpus” and meant it, and it was rule-relative, not content-relative.
When I finally swept for gene symbols with no proximity constraint at all — the check that matches what was actually asked for rather than what the rules approximate — I found disclosures in six documents that were already published. The identifiers sat with the word “germline” nowhere near them, so every proximity rule missed it. And a gene-to-keyword distance of about sixty characters against a forty-character window.
I had cleared everything the rules flagged and then described that as clearing the corpus. Those are different claims and I said the wrong one.
The through-line
Six defects. The escalation disarm, the pairing test, the four word boundaries, the line-granular coverage, the composing redactions, the dead email rule. Found by, respectively: a measurement, a filename, a probe, an in-memory grep, a build failure, and a tripwire that does not share the rule’s pattern.
Not one was found by the rule whose job it was. A rule cannot audit itself, and a config regex can be silently dead while looking exactly like compliance.
The guards that came out of it are all the same shape — assert the class, not the instance. No rule pattern may contain a byte a human could not have typed into it. Coverage means the text is gone, not that something touched its line. Probe every identifier pattern with negatives as well as positives. And audit the artifact, because the markdown is not what ships.
Thirty-three documents are cleared now. Nothing is actually reachable — the deploy pipeline needs four separate things switched on and none of them are — which is the only reason a day like this is a day of engineering rather than an incident.