Affidavits & Provenance
An affiant is one who swears to the truth of a written statement. Affiant borrows the metaphor directly: every AI-proposed database mutation becomes an Affidavit — a sworn statement, one field at a time, about where each value came from and how much the framework trusts it.
This page covers the two types that make that possible — Affidavit and ProvenanceTag — and the rule that makes them trustworthy: provenance is never optional, never omitted, and never invented.
The Affidavit record
Section titled “The Affidavit record”Affidavit is defined in Affiant.Abstractions.Models and is the core write-side contract of the framework:
public sealed record Affidavit( string OperationType, string EntityType, string? EntityId, AffidavitField[] Fields, float AggregateConfidence, string[] Warnings, bool RequiresConfirmation);OperationType— a domain-specific label such as"UpdateCustomer"or"CreateWorkOrder". It is a plain string, not a closed enum; hosts choose their own vocabulary.EntityType— the domain entity being mutated, e.g."WorkOrder".EntityId— the primary key of the entity being changed. It isnullfor create operations, where no identity exists yet.Fields— every field the mutation touches, each one anAffidavitField(below). This array is never empty for a real proposal — an Affidavit with zero fields swears to nothing.AggregateConfidence— the minimum confidence across all fields. A single low-confidence field drags the whole Affidavit down; the framework never lets one weak field hide behind a strong average.Warnings— business-rule violations detected while assembling the proposal, surfaced to the reviewer alongside the fields.RequiresConfirmation— whether this Affidavit needs a human reviewer at all. A host’sIApprovalPolicycan override this for auto-approved (Standing Order) cases — see Review Gate & Write Executors.
An Affidavit never causes a write by itself — see the Seven Normative Rules (Rule 3) and Tool Envelopes for how a WriteProposal carrying an Affidavit reaches a human before any row changes.
AffidavitField and mandatory fields
Section titled “AffidavitField and mandatory fields”Each entry in Fields is an AffidavitField:
public sealed record AffidavitField( string Name, object? Value, object? PreviousValue, ProvenanceChain Provenance, bool IsMandatory = false);Name— the domain field name, e.g."priority"or"customerEmail".Value— the proposed new value.PreviousValue— the current value, ornullfor a create operation where there is nothing to compare against.Provenance— the fullProvenanceChainfor this field, described below.IsMandatory— whether this field is required for the operation to make domain sense.
IsMandatory is not decorative. When a host implements ITaskInferenceStrategy to drive structured-output inference (see Tool Envelopes), it declares its field schema as a list of TaskInferenceField records:
public record TaskInferenceField( string Name, string JsonType, string Description, int? MaxLength = null, string? Pattern = null, IReadOnlyList<string>? Enum = null, bool Required = false);Every TaskInferenceField declared with Required = true must project to an AffidavitField with IsMandatory == true in the resulting Affidavit. Affiant’s compliance harness — see The Compliance Harness — checks this projection mechanically, so a strategy cannot silently drop a required field’s mandatory status on the way to the Evidence Card.
What IsMandatory does not do: it does not block approval by itself, and it does not force a value to be present. A mandatory field can legitimately arrive at review with Value unset and provenance ProvenanceSource.Empty — the field is still empty, but sworn to be empty rather than silently missing. Deciding whether an empty mandatory field is acceptable to approve is a reviewer-UI concern: the Evidence Card renders IsMandatory and ProvenanceSource.Empty together as a visual flag, and the human reviewer — not the framework — makes the call. This mirrors Rule 2 of the seven normative rules: the same data serves the LLM’s reasoning and the UI’s rendering without either side inventing meaning the other didn’t provide.
ProvenanceTag: the unit of trust
Section titled “ProvenanceTag: the unit of trust”Every field value the framework tracks carries a ProvenanceTag:
public sealed record ProvenanceTag( ProvenanceSource Source, float Confidence, string? Evidence, int? ConversationTurn){ public static ProvenanceTag Empty { get; } public static ProvenanceTag FromTool(string toolName, float confidence = 0.9f); public static ProvenanceTag FromInference(string fieldName, float confidence = 0.6f); public static ProvenanceTag FromDefault(string reason, float confidence = 0.3f); public static ProvenanceTag FromUser(string fieldName);}Source— which of the sevenProvenanceSourcevalues produced this value. Covered in full below.Confidence— a 0.0–1.0 score.FromUseralways assigns1.0f; the other factories default to0.9f(FromTool),0.6f(FromInference), and0.3f(FromDefault), each overridable via the optional parameter.Evidence— a human-readable explanation of why this source and confidence were assigned, e.g."Extracted from SearchCustomers"or"LLM inferred: priority". This is what a reviewer reads on the Evidence Card to understand why, not just what.ConversationTurn— which conversation turn produced the value, ornullfor sources that aren’t conversational (an external API lookup has no turn to point to).
The four non-Empty, non-UserStated factories exist so plugin and inference code never hand-constructs tags with ad hoc confidence numbers. FromTool is for values lifted directly out of a deterministic tool result; FromInference is for values an LLM inferred from conversational signal; FromDefault is for deterministic fallback rules; FromUser is for values the user stated directly, always at confidence 1.0. Reach for ProvenanceTag.Empty — not a hand-rolled tag — whenever there is genuinely nothing to swear to.
ProvenanceChain: the audit trail
Section titled “ProvenanceChain: the audit trail”A single tag captures a field’s current provenance. ProvenanceChain captures its history — the answer to “how did this field arrive at its current value?”:
public sealed record ProvenanceChain( ProvenanceTag Current, IReadOnlyList<ProvenanceTag> Prior){ public static ProvenanceChain From(ProvenanceTag tag); public ProvenanceChain Append(ProvenanceTag newer); public ProvenanceChain Merge(ProvenanceTag candidate); public ProvenanceChain AppendChain(ProvenanceChain other);}Prior is ordered newest-first. Append unconditionally promotes a new tag to Current and pushes the old Current onto Prior — used when a later turn definitively supersedes an earlier value, such as a reviewer’s amendment.
Merge is the more interesting operation, because it encodes the determinism hierarchy directly:
public ProvenanceChain Merge(ProvenanceTag candidate){ var candidateWins = candidate.Confidence > Current.Confidence || (candidate.Confidence == Current.Confidence && (int)candidate.Source < (int)Current.Source); // candidateWins: candidate becomes Current, old Current moves to Prior. // otherwise: Current is unchanged, candidate is recorded in Prior.}Higher confidence wins outright. When two tags tie on confidence, the tag whose Source has the lower enum ordinal wins — which is exactly the determinism hierarchy below, because the enum is declared in that order. The hierarchy is a single ordered list rather than a separate ranking table for exactly this reason: the ranking is the enum’s declaration order, and Merge reads it directly off (int)Source. Either way, the losing tag is never discarded — it lands in Prior.
Merge is what happens, for example, when a task-inference step produces an LLM-inferred value for a field the Context Fabric already holds from a higher-confidence source: the higher-confidence value wins, and the loser is preserved — not discarded — in the chain.
The seven-source determinism hierarchy
Section titled “The seven-source determinism hierarchy”ProvenanceSource is a closed, seven-value enum. The declaration order is the trust order, most deterministic first:
public enum ProvenanceSource{ UserStated, External, Computed, Conversation, Inferred, Default, Empty}| Source | Meaning | Default confidence from ProvenanceTag factories |
|---|---|---|
UserStated |
The user explicitly stated this value in chat — e.g. “my email is [email protected]”. Maximal trust. | 1.0 |
External |
Fetched from an authoritative external system: an API lookup, a database read, a third-party service response. | — (no dedicated factory; construct directly) |
Computed |
Derived by deterministic business logic — tax calculation, date math, priority-based SLA computation. Reproducible from inputs, not guessed. | — (no dedicated factory; construct directly) |
Conversation |
Mentioned in conversation context through a tool result, but not directly stated as a value by the user. | 0.9 via FromTool |
Inferred |
LLM-inferred from conversational signals rather than read or stated directly. Requires reviewer confirmation before committing. | 0.6 via FromInference |
Default |
A system default or fallback value applied when no conversational basis exists. | 0.3 via FromDefault |
Empty |
Provenance is unknown. Not “low trust” — the explicit, sworn statement that the framework has no basis for a value at all. | 0.0 via ProvenanceTag.Empty |
One deliberate omission: there is no HumanCorrected source. When a reviewer amends a field during approval, the amendment is recorded as a new tag with source UserStated — a reviewer’s correction is, evidentially, the same kind of ground truth as a user’s direct statement. The original tag is not discarded; it survives in the chain’s Prior list. The taxonomy stays clean this way: ProvenanceSource describes where a value came from, not what subsequently happened to it — that second question is what ProvenanceChain is for.
Rule 7: provenance is sworn, not assumed
Section titled “Rule 7: provenance is sworn, not assumed”This is the seventh of the framework’s Seven Normative Rules: every Affidavit field carries provenance, no exceptions. If a field’s origin is genuinely unknown, it must be tagged ProvenanceSource.Empty — never left untagged, never omitted from the array.
The reasoning is adversarial, not stylistic. A field with no provenance tag and a field tagged Empty look identical to a naive reader — but the framework does not let the first case exist at all: AffidavitField.Provenance is a non-nullable ProvenanceChain, and ProvenanceChain.Current is a non-nullable ProvenanceTag. There is no code path that produces a field without provenance; there is only the path that produces ProvenanceTag.Empty. Omission is not a lesser evil than a wrong tag — it is a category the type system refuses to allow, because a missing tag is indistinguishable from “the framework forgot to track it,” while an Empty tag is a positive, checkable assertion that nothing is known.
The reviewer-facing consequence: the Evidence Card renders provenance as a visual indicator per field — green for UserStated, amber for Inferred, grey for Default, and so on — so a reviewer approving an Affidavit is never guessing which fields are solid and which are the LLM’s best guess. A value present alongside ProvenanceSource.Empty is treated by Affiant’s own compliance tooling as a hollow signature — a value asserted with nothing sworn about its origin. The compliance harness enforces this mechanically for any host-authored inference strategy: a populated field with Empty provenance fails verification outright, because it is the exact shape of a regression where a filter produced values without tracking where they came from.
Where this fits
Section titled “Where this fits”Affidavit is what a WriteProposal carries out of a write tool — see Tool Envelopes. It is what the Docket holds while a mutation awaits review, and what the Evidence Card renders for the reviewer. Once approved, it is what a host’s IWriteExecutor receives — see Review Gate & Write Executors — as the only path by which a proposed value is allowed to reach the database.