Jonathan Asare
← All notes

AI assurance

An Approval That Predates Its Evidence Is Not an Approval

A release approval is valid only after its complete decision basis exists, and only while that basis remains current.

Checking that a sign-off came after the work is trivial. The hard part is defining the complete basis that must exist before anyone can sign.

Consider a release approval that passes every check you would normally write. The schemas validate. The artefact hashes match. The approver holds the right role and is active. Nothing is missing, nothing is stale, and the decision record says approved. Then you notice that the approver signed at eleven in the morning, and the retest that closed the one blocking finding was produced at half past.

Nothing in the pack needs to be false. The decision was signed before the evidence supporting its claimed system state existed.

What the sign-off actually rests on

The obvious fix is to require the decision to postdate the evidence it cites. That catches the case above and almost nothing else, because a decision rests on far more than the evidence records it names.

In EvalSeal, the gate computes a value I called the approval basis. The list of what feeds it is the interesting part.

Basis component Why it belongs What a signature before it means
Pack creation and change assessment The pack is the object under review; the change assessment fixes what class of change is being approved The approval covers a pack, or an assessment of the change, that did not yet exist
Governance policy, approval and effective times Sets the rules the decision itself has to follow The supplied governance policy was not yet approved or effective
Tailoring policy, approval and effective times Determines which controls apply to this system at all The supplied tailoring policy was not yet approved or effective
Change policy, approval and effective times Determines what a change of this class must satisfy before release The supplied change policy was not yet approved or effective
Evidence cited by the approval The direct factual grounds the approver named The signature describes a system state its own evidence had not yet established
Evidence behind any finding Findings the decision disposes of rest on source evidence of their own A finding was resolved on the strength of a result produced after the resolution
Verification of relied-on controls Where a treated risk relies on controls, their verification bounds the residual-risk acceptance A relied-on control was still unverified at the moment of signing
Residual-risk acceptance (risk-register.json) The release carries the risk remaining after treatment or explicit acceptance, and each recorded acceptance needs an authorised actor Someone approved a release before its recorded residual-risk acceptance
Recorded dispositions (risk-register.json, controls.json and findings.json) Risk and control not-applicable decisions and finding false-positive dispositions can remove blockers The approval predates a disposition that the recorded decision depends on

A finding marked risk_accepted is different. Its acceptance time still joins the approval basis, but that status does not resolve a blocking finding.

The gate takes the latest timestamp from that set and blocks if the decision came earlier. Approvers get the same treatment individually, with three distinct failure codes: signing after the decision or the gate run (APPROVER_TIME_INVALID), signing before the required evidence existed (APPROVER_PREDATES_EVIDENCE), and signing before the complete basis was established (APPROVER_PREDATES_BASIS).

The comparison is simple; deciding what belongs in the set is not. Each timestamp marks a dependency that had to exist before this approval could be valid. Every record can satisfy its schema while the chronology of the pack still contradicts the decision.

Some repositories already enforce a narrow version of this rule. When required reviews and stale-approval dismissal are enabled, GitHub dismisses an approval after a code-modifying commit arrives. That protects one changing input. A release decision has a wider basis to bind.

AI releases can make this boundary harder to hold because the candidate is often distributed across more than one artefact. EvalSeal binds the model and provider identity, prompt bundle, retrieval index, tool and permission configuration, application revision, and runtime environment. These inputs can change independently. Its reference policy calls for targeted reassessment after prompt or retrieval changes, and full reassessment after model, provider, or tool-permission changes. The temporal rule is not unique to AI, but this distributed candidate boundary creates several ways for an evaluation report to remain unchanged while the system it once described has moved.

The ceiling matters as much as the floor

The basis defines the earliest valid decision time. Evidence expiry defines the other boundary.

A control verification carries a validity window. EvalSeal caps it at the earlier of the evidence record’s declared expiry and its policy-defined maximum age. If a penetration test result expires in October, a control verified by that test cannot claim to be verified in November. The gate raises CONTROL_VERIFICATION_OUTLASTS_EVIDENCE and blocks.

For a treated risk that relies on verified controls, the acceptance cannot expire after the earliest verification_valid_until date among those controls. That is RISK_ACCEPTANCE_OUTLASTS_CONTROL.

EvalSeal applies the same ceiling to the approval itself: next_review_at cannot fall after the earliest expiry in its supporting basis. Basis expiry includes policy review dates, evidence freshness cutoffs, control-verification validity ends, risk or finding acceptance expiries, and open-condition due dates. If the approval exceeds that limit, APPROVAL_REVIEW_AFTER_BASIS_EXPIRY blocks.

The mismatch appears when each owner chooses a local date without checking dependencies. The evidence owner sets a validity period; the control owner sets a verification interval; the risk owner sets an acceptance period. Each date can look reasonable alone while their combination claims support after an upstream record has expired. I enforce these constraints with tests named for the behaviour rather than the code path, so the suite reads as a list of things the gate refuses. Together, the two boundaries turn assurance into a dependency graph with time on it: nothing may claim validity before what it rests on exists, or after that basis stops being valid.

Temporal integrity / 01

An approval sits between a floor and a ceiling

It must follow everything it rests on and stop before any dependency expires.

01 / Floor

Complete the basis before signing

The latest required event becomes the approval basis.

  1. 01Policy effectiveThe governing rule is in force
  2. 02Evidence producedThe observed result exists
  3. 03Control verifiedThe relied-on control is checked
  4. 04Risk acceptedThe remaining risk has authority
Signed before the basisBlockedThe decision predates something it relies on.
Signed after the basisEligibleEverything it relies on already exists.
02 / Ceiling

Do not outlive a dependency

A dependent claim must expire no later than the evidence beneath it.

EvidenceValid until expiry
Supported period
ControlClaims a longer period
Unsupported extension

Evidence expiresControl verification is blocked beyond this point

The constraint continues: an acceptance for a treated risk may not extend beyond the earliest validity end of the verified controls it relies on.

The complete basis sets the earliest valid approval time. The earliest dependency expiry limits how long that approval can remain valid.

EvalSeal adds two related checks. Within each required evidence type, it selects the record or records with the newest produced_at timestamp; any non-passing result at that timestamp blocks. Every failed evidence record must also be linked to a finding, and a finding marked verified_fixed requires a distinct, passing retest that does not predate the source evidence.

What this cannot do, and why that is stated

Every timestamp above is supplied in a record that EvalSeal does not independently authenticate. The gate can prove that the bytes it inspected match the recorded hashes, and that the records satisfy the configured rules. It cannot prove that a report tells the truth, that a test harness touched anything resembling production, that an actor was authenticated outside the file, or that deployment used the approved bytes.

The trust-boundary document therefore describes EvalSeal’s value as procedural evidence binding rather than independent assurance. I would not accept a release gate that leaves equivalent limits implicit, because reviewers cannot tell which claims its green result does not support. Here, the dangerous failure is interpretive: someone reads structural consistency as evidence that the reports were truthful or deployment matched the candidate.

I built a matching refusal into the code. The worked example ships as a fictional pack. Run it without an explicit flag and it is rejected. Run it with the flag and an approving outcome comes back as SIMULATED_APPROVE, with a separate authorizes_deployment field set to false. Those fields make the distinction explicit; a surrounding workflow must still honour them rather than treating any approving result as deployment authority.

If the threat model includes falsified chronology, timestamps must be authenticated by a system the subject cannot rewrite, such as a protected CI log that captures the event when it occurs or a trusted timestamped attestation of that event. That expands the trust system well beyond validating JSON. For an internal process whose participants are not adversaries, record-supplied timestamps may be the right trade. The balance changes when a decision must withstand independent audit or adversarial tampering. I am not confident where that line sits, and it moves the moment an auditor rather than a colleague is the reader.

Try it against one approval you already have

Take the most recent significant sign-off in your system. A change advisory record, a security exception, a model release, a production access grant. Anything with a named approver and a date.

Write down every artefact that approval depends on, not just the ones it links to. Include the policy version in force, the standard it was assessed against, the test results, the compensating controls, the accepted risks, and any exception someone granted along the way. For each one, find its timestamp.

Then sort them and compare the signature with the latest basis timestamp.

If the signature came first, the current pack claims a basis that was not yet established at sign-off; require a new review and signature. Next compare the approval’s planned review date with the earliest expiry, required review date, or open approval-condition due date in its supporting basis. If the approval review falls later, shorten it or refresh the upstream basis before relying on the decision.

Sources

  1. EvalSeal, jonathanjasare/evalseal, Apache-2.0. The implementation references below are permalinks to commit db92c14.