Measured, not asserted
Published
An expedited safety report is not due from the day the event happened. It is due from the day the sponsor became aware of it. Both dates sit on the record, both are correct, and the deadline arithmetic is right either way -- so which of two correct dates a correct subtraction runs from decides whether a report filed on day 46 was inside a fifteen-day window or nine days outside it. That turned out to be the loud failure in this file. Three other renames were quieter, and one of them reported a death as filed on time.
Six events, one genuinely late
The fixture is a safety database with six adverse events across a trial. Five meet a seriousness criterion and are on an expedited track: seven days for a death or a life-threatening event, fifteen days for everything else. The correct answer is four reports in time and one late -- a death that reached the authority nine days after the sponsor knew about it, on a seven-day track.
That is the number the whole file is about. Every arm below is scored against it: expedited=5 in-time=4 LATE=1.
Protection on its own moved nothing
All five areas of this pass ran through five presets before any renaming -- ES5, modern, the emit gate's option set on both targets, and the string transforms. Twenty-five comparisons, every one byte-identical to the unprotected run, under a guard that runs each original twice and refuses a fixture that will not reproduce itself.
Nothing below is caused by protecting the code. Every result required member renaming aimed at a property name that both the sponsor's own records and the installed reporting engine read.
The loud one first: the clock moves to the wrong correct date
Renaming the option that says which date the clock runs from reverts it to the vendor's default, which is the onset date. That is not an unreasonable default -- it is the date a library can be sure exists -- and it is the wrong one for this purpose.
The result is in-time=2 LATE=3. Two reports that were filed comfortably inside their window are now recorded as late: one at eighteen days, one at thirty-two. The genuinely late death is still late.
This is the direction you want. Three late reports where there should be one is a finding that generates work: somebody pulls the case files, compares the dates, and discovers within a week that the clock is anchored wrongly. Over-reporting a compliance failure is a bad day. It is not a bad year.
The quiet ones: nothing was ever late
Renaming the list of seriousness criteria reverts it to the vendor's empty list, and an empty list qualifies nothing. The output reads expedited=0 not-expedited=6, and the line for the death reads AE-05=NOT-EXPEDITED[outcome=death not in the seriousness criteria]. No expedited report is due, none is filed, and none is late.
The sponsor's own reporting rule passed. It checks that every event meeting a seriousness criterion was assessed against a deadline, and it reported 6 event(s) reviewed, 0 serious, all assessed against a deadline. Every clause of that sentence is true.
Renaming the outcome field on the event records instead reaches the same place from the other side of the boundary: nothing matches any seriousness criterion, so nothing is expedited.
The third route is the quietest of all, and it is the one worth carrying away.
The quietest route leaves every count intact
Renaming the option carrying the deadline table reverts it to an empty table. An empty table means no deadline applies, and no deadline means nothing can exceed one. The output reads expedited=5 in-time=5 LATE=0.
Compare that with the seriousness-criteria arm. There, the expedited count collapsed to zero -- a number so obviously wrong that a safety officer glancing at a monthly summary would query it. Here, five events are still correctly identified as serious, still correctly placed on the expedited track, still individually listed. The only thing that moved is the deadline they were measured against, and the late count went from one to nought.
Renaming the table's keys rather than the table produces the same figures. The keys are the outcome strings -- death, life-threatening, default -- and a lookup that misses is not an error. It is a fallback, and the fallback is 'no deadline'.
A safety database that reports five expedited events and zero late filings looks like a well-run trial. It is the single most reassuring line the system can print.
What failed closed
The dates themselves failed loudly. Renaming the awareness date makes every expedited event unassessable and the file reports so explicitly. Renaming the reported date pushes all five onto the late list.
That is the seventeen-shapes-and-counting contrast again, and it holds here on adjacent lines of one record: the awareness date is a value and losing it fails closed; the option naming which date to use is a meaning and losing it fails open. One field is data and one is a statement about what the data is for.
The audit question that returns the wrong answer
Ask this system whether expedited reporting is configured and it says yes, in every arm. The tracks are defined, the rule is installed, the flag requiring the sponsor's own review is set, and the review runs and passes.
Ask it how many expedited reports were late and it says nought, which is the answer a sponsor wants and a regulator will eventually test against the case files. The gap between those two questions is the whole of this article.
If any part of your safety workflow runs in a browser -- a site-facing event form, a triage screen, a dashboard that computes the clock -- then the data flowing through it is special-category health data, and the script-inventory discipline GDPR already asks for is the same discipline that answers this: know exactly which build is computing the deadline, and assert the deadline rather than the configuration.
What to do about it
Assert the late count, not the setup. One fixture case with a known awareness date, a known reported date and a known track, asserted to be late, fails in every silent arm in this article. A test that checks the tracks are configured passes in all of them.
Make a missing deadline a refusal. An event on an expedited track with no deadline found should raise, not sail through. The same goes for an event whose outcome does not match any seriousness criterion: that is a case for a human, not a default to non-serious.
And keep the awareness date on the same record as the event, written by the same party, at the same time. A clock anchor that lives in a different key from the thing it anchors is separable, and anything separable can be separated.
Frequently asked questions
Did obfuscation change any safety determination on its own?
No. Five areas ran through five presets and every protected output matched the unprotected run. Every failure here required member renaming aimed at a name the installed reporting engine also reads.
Which rename was the dangerous one?
Renaming the option carrying the deadline table. Five events were still correctly identified as serious and correctly placed on the expedited track -- and every one of them read as filed in time, because a table with no entries means no deadline applies.
What happened to the death that was genuinely late?
In the seriousness-criteria arm it read 'NOT-EXPEDITED[outcome=death]'. In the deadline-table arm it read IN-TIME. It was reported nine days after sponsor awareness on a seven-day track.
Did the sponsor's own reporting rule catch anything?
No. It passed in every silent arm and told the truth while doing it -- '6 event(s) reviewed, 0 serious, all assessed against a deadline'. It counts events assessed; it does not know what they were assessed against.
Why does the clock-anchor rename fail in the safe direction?
Because it moves reports onto the late list rather than off it. Three late reports where there should be one gets investigated within a week. It is the failure you want.
Is this specific to obfuscation?
No. A dropped key over a serialisation hop, a library upgrade that renames an option, or a hand-edited configuration file all produce an unreadable deadline table. Obfuscation is one way to reach a state that has several doors.
What is the cheapest test that would have caught this?
One known case asserted to be late, end to end. That fails in every silent arm here. Asserting that the seven-day and fifteen-day tracks are configured passes in all of them.
Related reading