An answer needs a state: confirmed, to check, or gap
A bare "yes, it's encrypted" cannot be defended a year later. A fact with a state, an owner and an expiry date can.
20 September 2026

The problem with a value that is only a value
You ask a client whether data is encrypted at rest. Someone says yes. You write it into a security overview, a data processing agreement, a trust page and, three weeks later, into answers 14, 31 and 52 of a questionnaire from their largest customer.
Eleven months on, that customer's vendor manager asks you to confirm it. You
open your notes and find the string yes.
You now cannot tell the difference between four situations that look identical
on disk: the client's CTO checked the database configuration and said yes; a
model pulled yes out of an existing policy you uploaded; an account manager
guessed because the question felt like it wanted a yes; or you copied the answer
from a similar client last spring and never went back. The fourth is the
dangerous one, because it looks the most like work.
A value alone has no defence. A fact with a state, a person and a date does.
Four states, and what each one licenses you to do
The fact store in this product keeps one row per client per slot, and the row
carries a state: empty, extracted, confirmed, stale. The states are not
decoration; each one licenses a different action.
empty is the honest zero. Nothing has been said about this slot. Worth
noting what also counts as empty: a suggested default by jurisdiction. The
product can propose a typical answer for a client registered in Germany, but the
proposal sits on screen unaccepted, and an unaccepted default stays empty. A
default that silently became an answer would be exactly the guess we are trying
to eliminate, only faster and at scale.
extracted means something produced a candidate value that no human has
stood behind. Three things land here. A model reading the interview transcript,
where confidence above 0.8 records the value and anything lower turns into a
clarifying question rather than a write. An imported document, where a policy
and a subprocessor list the client already has can close a large share of slots
in one pass. And copying from a similar client — "fill this in like the other logistics one" —
which moves infrastructure, process and retention answers across, and marks
every one of them extracted, never confirmed. That last rule is the one
people argue with, and it is the one that matters most: the fastest way to
manufacture a defensible-looking lie is to inherit forty answers from a client
that resembles this one.
confirmed carries confirmed_by and confirmed_at. A person put their
name on it. This is the only state that a document may be generated from, and
the only state that lets a questionnaire answer be marked confirmed. Once a fact
is confirmed, a later extraction cannot quietly overwrite it — a new candidate
value from a model does not downgrade a human confirmation. Changing a confirmed
value is a deliberate act, and it has consequences downstream, which is the next
section.
stale is a confirmed fact past its valid_until date. It is not wrong. It
is unwitnessed. The distinction is the whole point: you are not telling the
client "your encryption is gone", you are telling yourself "nobody has looked at
this since March".
Facts do not all have the same shelf life
A registered address and a headcount change rarely, and when they change, somebody in the business knows. A production access model, an offboarding procedure, an MFA policy and a backup schedule change whenever an engineer changes them, which is to say without an announcement.
So validity is set by the group the slot belongs to. Facts in the infra and
process groups expire six months after confirmation. Everything else —
company, product, retention, vendors, and the regime-specific groups — expires
in twelve. The rule is deliberately crude. A more elaborate per-slot schedule
would be more accurate and would not survive contact with a portfolio of two
hundred clients, because nobody would be able to predict what the system was
about to ask them.
What this buys is a query you could not write against a JSON blob. One index on firm, slot and state answers the questions that turn up in the first month of running a practice on a shared store: which clients have no enforced MFA, whose confirmations have expired, what changed since the last generation. On a client list, the earliest expiry date across that client's confirmed facts is a single column — the date the oldest part of their documentation stops being witnessed. That column is an annual review, scheduled by the data rather than by somebody remembering.
A negative answer is a closed fact
mfa_enforced: false is not a blank. It is an answer, and it closes the slot
the same way true does.
This sounds obvious and is routinely got wrong, because a product that treats "no" as an unfinished state quietly pushes everyone toward "yes". The interview must be able to say: this is not in place, we will write it honestly and add a plan. Generation then produces the honest sentence — the measure is not currently in place, it is planned — rather than dropping the section, and the editor will refuse an instruction to write the opposite. The wording it gives back is that the client's own data says otherwise, and if the situation has changed, update the interview answer and the section is regenerated.
The reason is not squeamishness. The person on the other end of a vendor questionnaire reads these for a living. A package where every security control is present and none has a date is less credible than one that says two things are missing and names when they will be addressed.
What goes into a document when the fact is missing
Nothing plausible. That is the whole rule.
When a required fact is absent, generation writes [[GAP:slot_key]] into the
body and collects the gaps onto the document record. The marker resolves to the
interview question for that slot, so the rendered output does not say "gap" — it
says what to go and ask. In an exported file it appears as a bracketed
instruction to complete, carried through into the DOCX and PDF rather than
stripped at the door. The consultant accepted responsibility for it at the
export gate; hiding it afterwards would defeat the gate.
The alternative — a model writing a reasonable-sounding sentence about a control nobody described — is the failure mode that ends a consulting relationship, and it is invisible at review time precisely because it reads well.
The same logic governs questionnaire answers, with three states of their own.
confirmed requires a pointer to a specific section of one of the client's own
documents; an answer that claims confirmed status without a valid source is
downgraded to review in code, not by asking the model to behave. So is an
answer that introduces a certification claim — SOC 2, ISO 27001, PCI DSS,
HIPAA, the word "audited" — that appears nowhere in the client's facts or
document excerpts. review means drafted but resting on general wording.
gap means the client does not have this, written plainly, with a plan.
Answers pulled from the firm's own reusable library land as review, not
confirmed, because a template that fitted the last client is a candidate for
this one, not evidence about it.
The question this is actually answering
Every one of these mechanisms exists to answer one question, asked by an auditor or a corporate customer or your own client six months from now: how do you know?
"It's in our records" is not an answer. "It was confirmed by this person on this date, it is valid until that date, and here is the document section the questionnaire answer was drawn from" is. That chain is what clausebench stores per fact, and it is the difference between documentation you can stand behind and documentation you merely produced.