The signature was valid the day I made it. Three months later it was invalid, and nothing in the system had changed.
A counterparty's verifier rejected a batch of signed documents. My verifier accepted the same files. Same bytes, two verdicts, which cannot happen, so one of those statements was wrong.
Mine was. The bytes were not the same.
Between the step that produced the document and the step that stored it sat a normalization pass. Not a feature, not a decision: a serializer option left at its default. Signing ran on the in-memory document. Storage happened after normalization. At creation I verified the signature against what I had just signed, so every test passed and every integration check passed. The discrepancy lived only in the artifact I kept.
And nobody verifies a signed document on the day it is signed. They verify when a deal closes, or when someone disputes it. By the time the system tells you it is broken, the broken documents sit in other people's hands already, and the fix is not a deploy: it is going back to real counterparties and asking them to sign again.
What I do differently now: the signature gets computed over the stored artifact, never the in-flight one. A job re-verifies stored documents against stored signatures on a schedule, not once at creation. Any transform sitting between producing and persisting I treat as signature-breaking until proven otherwise.
The bugs that cost the most are quiet. Detection is the part the business process defers, and the gap between the write and the first real read is the actual blast radius.
I write up more of these failure modes here: https://polycratia.com/c/efba9f13
Probably worth asking where your system still assumes that what you validated is what you stored...