TLDR
- Freshness is not the age of a record. It is whether a proposition remains valid, authoritative and admissible for this decision at the time consequence occurs.
- The mechanism requires two time axes, typed supersession and a decision frame that separates what was true, what was known, what governed and what is permitted now.
- Consequential actions should carry evidence, policy, identity and world-state versions into an atomic action-time check, rather than trust a model’s earlier recommendation.
- Bitemporal stores and supersession graphs add cost and cannot repair a wrong source, missing history or genuinely disputed validity. Use them where temporal confusion changes outcomes.
- Replace “prefer the latest” with a temporal entitlement contract: select the governing claim, record its limits, revalidate authority, and reject execution when any premise changed.
The approval that remained true after it stopped counting
At 10:04:08, a payment screening service marks a corporate payment as a possible match and the orchestration service places a hold. At 10:06, an operations analyst clears the alert. At 10:09, the external feed corrects the candidate record. At 10:12, a relationship manager asks an agent why the payment was held and whether it can now be released.
The agent has excellent retrieval. It can quote the original screening result, the analyst’s clearance, the corrected source record and the applicable procedure. Nothing is fabricated. Yet one fluent answer can still make three incompatible moves: use the correction to imply the workflow should have known more at 10:04; use the old possible-match assertion as if it were current at 10:12; and reuse an approval captured before the operator’s release authority changed.
The central error is easy to miss because every sentence can be literally true. The system did receive a possible match. The analyst did approve release. A newer procedure was published. The problem is that truth alone does not confer present authority. A claim is action-eligible only inside a declared temporal frame and only while the authority that makes it usable still holds.
This is the narrower question behind the broader architecture described in Governed Context Architecture. Reconstruction can fetch a current value. Paper 111 asks the harder control question: when may that value, or an earlier value retained for history, support a consequential action?
The answer at 10:12 therefore needs two ledgers. “As acted” explains that the hold followed the screening result and procedure available at 10:04:08. “Current evidence” says the candidate was later cleared and corrected. A proposed release then needs a third operation, distinct from explanation: a fresh check that the payment state, policy version, operator identity, purpose and delegated authority still permit the effect.
Part I
Why “latest” fails before reasoning begins
Most retrieval systems treat time as a ranking feature. A recently modified page receives a boost; an older page is penalised; the top few passages enter context. That is useful for news search. It is unsafe when the result can authorise an effect. The newest document may be future-effective, the newest database row may correct an earlier period, and the newest approval may have been issued by someone whose authority has since been withdrawn.
Recency orders artefacts. It does not determine applicability. Applicability is a relation among a proposition, a decision, a scope and a time. It asks whether this exact claim governs this exact case at the relevant instant. The same source version can be admissible for a historical explanation, inadmissible for a current recommendation and essential for a counterfactual replay.
A signature answers who attested to bytes. A timestamp answers when some event was recorded. Neither alone answers whether a permission still governs. OAuth token introspection exists for a related reason: a protected resource can ask an authorisation server whether a token is currently active, including whether it has expired or been revoked.8 The OpenID Continuous Access Evaluation Profile goes further by standardising events that can attenuate access after a session begins.9 These mechanisms do not supply a complete decision model, but they expose the key principle: past issuance is not present entitlement.
The clocks hidden inside one “updated at” field
Temporal database research separates valid time, when a fact holds in the represented world, from transaction time, when the database stored that fact. A system supporting both is bitemporal.1 For consequential agents, two further clocks usually matter: decision time, when a recommendation or control was formed, and effect time, when the external state change commits. Publication and observation times often deserve their own fields as well.
These clocks can coincide, which is why weak designs survive simple tests. A payment event may be observed, recorded and acted on within milliseconds. The failure appears when one clock moves independently: a correction arrives late, a policy is published before its effective date, an approval expires during a long-running task, or an action retries after state changed.
A clock is a question, not a column
Every temporal field should answer an interrogative sentence. valid_from asks when the proposition is asserted to hold in the represented world. observed_at asks when a source sensed or received the event. recorded_at asks when this version entered institutional knowledge. decided_at fixes the evidence and policy used by a decision. effect_at marks the commit of an external change. A policy’s effective_from is a valid-time assertion about governance, not a synonym for publication.
Collapsing those questions creates two opposite errors. Hindsight leakage imports a correction that arrived later into an account of what an earlier actor could know. Stale carry-forward preserves an earlier approval or assertion after its validity, scope or authority ended. The first rewrites explanation. The second authorises action with an expired premise. A system can avoid one and still commit the other, so “we retain history” and “we fetch current data” are not sufficient controls.
A useful causal test moves one clock while holding the bytes fixed. Delay ingestion by an hour: historical world validity should not move, but the then-known view should. Publish a policy earlier without changing its effective boundary: current governance should not move. Delay execution until after revocation: the decision receipt should remain explainable, while the effect must fail. When moving a clock changes the wrong answer, the temporal contract is underspecified. Not every source needs five physical columns, but any clocks derived or equated by design need an explicit invariant that can be tested.
Internet timestamp standards help encode instants consistently, and RFC 9557 can attach time-zone information to an RFC 3339 timestamp.11 They cannot decide whether a field means occurrence, observation, ingestion, approval or effectiveness. That semantics belongs in the source contract. Writing 2026-07-22T10:04:08.123Z with millisecond precision does not rescue a field populated from the wrong clock.
Minimal worked example: a fee waiver
Consider a fee waiver approved for account A from Monday 09:00 until Friday 17:00. The approval is recorded on Tuesday because the case queue was delayed. On Thursday, a current-state question should treat the waiver as valid even though its database age is only two days and its real validity began earlier. On the following Monday, the same approval remains authentic evidence that a waiver existed, but it must not suppress a new fee.
A single created_at field cannot answer both questions. The minimum useful record needs a valid interval, a recorded interval or immutable history, the account and fee scope, the approving authority, and any cancellation or replacement relation. The query also needs a target: “was a waiver valid on Thursday?” differs from “what did the system know on Monday morning?”
Allen’s interval algebra formalised relations such as before, overlaps, during and meets, which remain useful when dates are uncertain or boundaries matter.2 In implementation, half-open intervals such as [start, end) avoid double membership at a clean handover. If a policy says only “effective Friday”, the system must preserve date-level uncertainty or apply an approved local-time convention. It should not silently invent midnight UTC.
Part II
flowchart TD A["The approval that remained<br/>true after it stopped counting"] --> B["Why “latest” fails before<br/>reasoning begins"] B --> C["From temporal facts to<br/>temporal entitlement"] C --> D["Build the check where<br/>consequence occurs"] D -. "re-test" .-> A classDef first fill:#0a1424,stroke:#D95545,stroke-width:2px,color:#e6f3ff classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff class A first class B,C,D step
From temporal facts to temporal entitlement
Bitemporal storage answers a vital evidential question: what proposition was considered valid for the world, and when was that proposition present in the institution’s knowledge? It does not, by itself, answer whether the proposition may authorise a particular action. For that, the system needs a third concept.
Temporal entitlement is the time-bounded eligibility of a claim, grant or policy version to support a specified decision or effect. It joins evidence semantics to authority semantics. The claim must be valid for the target, known within the permitted cut-off, applicable to the case scope, issued by an accepted authority, unsuperseded for that use, and still compatible with identity and world state when the effect commits.
This is broader than checking token expiry. A principal can hold a valid token while the evidence supporting the requested action has been corrected. A claim can be current while the principal’s purpose no longer permits its use. A policy can be effective while the downstream system has not activated the matching control. Temporal entitlement therefore spans intent, identity, world state, context, reasoning, authority, action, evidence, outcome, and release and recovery. Collapsing those objects into “context” hides the exact premise that expired.
A compact formal test
Let c be a claim, d a decision frame, t the target valid time, k the knowledge cut-off, s the case scope and p the declared purpose. A useful admission predicate is:
Valid tests the world or policy interval. KnownBy prevents later evidence from leaking into a historical-cause account. Governs resolves replacement, amendment, suspension and scope. Authoritative checks source class, identity, delegation and purpose. Tightening any term reduces the admitted set. Moving t backwards can restore a predecessor for replay; moving k backwards can exclude a later correction.
For an external effect x at commit time te, admission is still insufficient:
The final equality closes the race between reasoning and action. If the account, payment, policy, approval or entitlement version changed, the action service rejects the request and the system recompiles. The model may propose again; it does not get to waive the mismatch.
Supersession is a scoped graph
A boolean is_latest assumes one total ordering. Real governed artefacts rarely behave that way. Version 7 can replace version 6 for payments initiated after 11:00 while version 6 still governs earlier payments. An amendment can replace clause 4 and leave clauses 1 to 3 untouched. A suspension can temporarily disable one branch, followed by reinstatement. A source correction can alter current truth retroactively while the original assertion remains evidence of what a workflow knew.
Represent these as typed edges: replaces, amends, corrects, retracts, suspends, reinstates and consolidates. Each edge needs a predecessor, successor or notice, effective condition, recorded time, scope predicate and authority. Crossref’s guidance for corrections and retractions illustrates the value of linking updates rather than silently overwriting the scholarly record.13 A bank needs different vocabularies, but the recordkeeping principle transfers.
Why the edge type changes the answer
The edge vocabulary is causal, not decorative. A correction says that a predecessor should no longer support the corrected proposition for a stated valid interval, while preserving that the predecessor was once recorded. A replacement installs a successor from a boundary without denying that the predecessor governed before it. An amendment changes a selected clause, population or channel. A suspension temporarily removes authority; reinstatement restores it from a later boundary without erasing the suspended interval. A retraction withdraws support and may leave no successor at all.
Misclassifying an edge changes the selected claim even when every node and timestamp is correct. Treat policy v7 as a retroactive correction and the 10:04 decision is judged under a rule that was not yet effective. Treat the identity correction as a future replacement and the known-wrong candidate can remain eligible until the correction’s recording time, even though the source says its corrected validity reaches back. Scope matters just as much: an amendment for international payments must not disqualify the domestic clause it never touched.
A supersession edge is itself a governed assertion. It needs an issuer, recorded time, effective condition, reason, scope and, where relevant, a precedence rule. Two disagreeing sources do not automatically supersede one another. They may form an unresolved conflict that requires source ranking, independent evidence or human disposition. Turning every contradiction into “latest wins” hides a contest of authority inside a sort operation.
W3C PROV provides useful terms for entities, derivation, generation and invalidation, while OWL-Time provides vocabulary for instants, intervals and their topological relations.45 Neither standard defines a bank’s complete applicability rules. They are building blocks for provenance and time, not substitutes for policy scope, source authority or execution control.
Freshness is a contract per proposition
A global “maximum age” is attractive because it is measurable. It is also semantically weak. A balance may require a committed read seconds before action. An incorporation date can remain reliable for years unless a contrary event arrives. A policy clause is fresh when the correct effective base and active amendments are assembled, even if the PDF was downloaded months ago. A derived summary becomes stale whenever a load-bearing dependency changes, regardless of the summary’s own timestamp.
HTTP caching offers a useful, limited analogy: RFC 9111 distinguishes a response that is fresh enough to reuse from one that needs revalidation.10 Evidence has richer conditions. Byte identity does not prove continued applicability, and a source can remain unchanged while its authority is suspended elsewhere. The contract therefore belongs to the proposition and decision, not merely the object.
NIST’s ABAC guidance treats environmental conditions, including time, as inputs to access decisions and notes that such conditions can change rapidly.6 XACML’s current-time attribute is fixed for a request context, which is sensible for consistent policy evaluation.7 A long-running agent still needs a new context or downstream guard at effect time. Otherwise a correct permit decision becomes an expired permit in transit.
Part III
Build the check where consequence occurs
A temporal evidence envelope
The agent should not receive a bare passage plus a generic timestamp. Each load-bearing assertion needs an envelope that separates source content from claims about its status. A compact envelope names the proposition, value, source and immutable version; valid interval; recorded time; authority class; scope; supersession edges; retrieval receipt; derivation lineage; and uncertainty. A derived summary carries the versions of every source assertion it depends on. It does not inherit their authority merely because it paraphrases them accurately.
The envelope supports distinct answer types. current_state selects claims valid at the target using knowledge available by the current cut-off. historical_cause freezes what the actor or workflow could know at the event. historical_reperformance reapplies the governing rule and transformation versions. change_since compares two complete frames. counterfactual substitutes named assumptions while freezing the rest. The output schema should prevent a model from sliding between them without a declared transition.
The model may narrate a temporal join. It should not perform the join by comparing prose dates. A deterministic context compiler queries the temporal store and policy registry, resolves scope and supersession, checks source capabilities, and emits admitted assertions with rejection reasons. The model reasons over that result. The decision receipt preserves both the admitted set and the material exclusions.
Carry versions into the action contract
Retrieval and reasoning usually precede execution. During that gap, the payment can be released by another operator, the account can acquire a hard stop, a policy can be suspended, or the principal’s authority can be revoked. This is a time-of-check to time-of-use race, the general weakness catalogued as TOCTOU by MITRE.12 Prompting the agent to “double-check” reduces nothing unless the check reaches the authoritative system and binds the effect.
The action proposal should therefore carry preconditions: expected resource version, expected policy release, principal and delegated grant, purpose, critical assertion versions, idempotency key and deadline. The action service evaluates policy and preconditions in the same transactional boundary as the state change. A mismatch returns conflict or recompile_required. It never means “use the earlier approval anyway”.
Zanzibar’s authorisation design is relevant because it preserves causal ordering of user actions and provides external consistency amid access-control changes.3 The transferable lesson is not to copy its data model blindly. It is that an authorisation check must respect the causal state produced by earlier revocation or sharing changes. In an agent system, the same discipline must cover evidence and policy versions as well as relationship tuples.
Separate admission, proposal and commit
The control is clearer when split into three deterministic gates. The admission gate compiles claims for the declared frame and records why candidates were excluded. The proposal gate checks whether the requested action is allowed in principle for the principal, purpose, resource and effective policy. The commit gate rereads authoritative state, current authority and load-bearing versions inside the effect boundary. A failure at each gate has a different meaning: unsupported context, prohibited proposal, or changed conditions.
This separation prevents a common recovery mistake. When commit returns a version conflict, the system should not retry the same payload until it succeeds. It must recompile evidence and policy, reconsider the proposal and obtain any renewed confirmation. Only transport failures with unchanged idempotency and preconditions qualify for a safe retry. If the transport times out after acceptance, the outcome is unknown until an idempotency lookup or independent readback proves whether the effect occurred.
Revalidation can still be selective. Payment state and a hard-stop grant may require live reads; a company’s incorporation date may be reused unless a contrary event invalidates it; a derived summary can remain usable only while every load-bearing source version remains admitted. The decision receipt should bind the frame, admitted claim versions, material exclusions, policy release, principal, purpose and expected state. The effect receipt should add the committed version, idempotency result and postcondition readback. Reasoning remains stochastic, but temporal authority becomes inspectable at the point where it matters.
Production-shaped worked example: reperform, then act
Return to Harbour Bank. At 10:04:08, the screening assertion is “possible match”, policy v6 is effective and the workflow applies a hold. At 10:09, a correction says “not the customer”, with a valid interval that reaches back to the original event. That correction changes the institution’s present assessment of the identity proposition. It does not alter what the workflow had available at 10:04:08.
A historical-cause packet therefore selects the original assertion because its knowledge cut-off is 10:04:08. A current-state packet selects the correction because its cut-off is 10:12 and the typed correction edge disqualifies the predecessor for present support. Policy v7, though recorded at 09:30, remains future-effective until 11:00, so v6 governs both the original event and a 10:12 release action. The two packets are both correct because they answer different questions.
The release action is evaluated again. The action service reads payment version 15, checks that the alert is cleared, evaluates the operator’s current grant for the declared purpose, resolves the effective policy graph and tests the absence of a new stop. If the proposal was built on payment version 14, it fails fast. A reviewer sees an explicit conflict rather than a silently stale success.
Executable artefact: a temporal claim evaluator
The following dependency-free Python programme encodes the article’s central distinction. Assertions have valid time and recorded time. Supersession edges have their own effective and recorded times. A historical frame sees only what was known by its cut-off. A current frame admits the correction. The action evaluator separately checks a current grant, governing policy and optimistic world-state version.
Assumptions are deliberately bounded. Scopes are exact strings rather than a full policy language; intervals are UTC and half-open; every proposition must resolve to one admissible assertion; and the action service is represented by an in-memory function. Expected output includes one permitted action and one denied action after grant expiry and state change.
Runnable Python: temporal claim evaluator
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Iterable
import json
def at(hms: str) -> datetime:
return datetime.fromisoformat(f"2026-07-22T{hms}+00:00")
@dataclass(frozen=True)
class Interval:
start: datetime
end: Optional[datetime] = None
def contains(self, instant: datetime) -> bool:
return self.start <= instant and (self.end is None or instant < self.end)
@dataclass(frozen=True)
class Assertion:
id: str
proposition: str
value: str
scope: str
authority: str
valid: Interval
recorded_at: datetime
source_version: str
@dataclass(frozen=True)
class Supersession:
predecessor: str
successor: str
kind: str
scope: str
effective_from: datetime
recorded_at: datetime
@dataclass(frozen=True)
class DecisionFrame:
valid_at: datetime
known_at: datetime
scope: str
@dataclass(frozen=True)
class ActionRequest:
principal: str
action: str
resource: str
purpose: str
action_at: datetime
expected_state_version: int
expected_policy_id: str
@dataclass(frozen=True)
class ActionResult:
allowed: bool
reasons: tuple[str, ...]
def scope_applies(edge_scope: str, request_scope: str) -> bool:
return edge_scope == "*" or edge_scope == request_scope
def disqualifying_edge(
assertion: Assertion,
frame: DecisionFrame,
edges: Iterable[Supersession],
) -> Optional[Supersession]:
applicable = [
edge for edge in edges
if edge.predecessor == assertion.id
and scope_applies(edge.scope, frame.scope)
and edge.recorded_at <= frame.known_at
and edge.effective_from <= frame.valid_at
]
return max(applicable, key=lambda e: (e.effective_from, e.recorded_at), default=None)
def admissible(
assertion: Assertion,
frame: DecisionFrame,
edges: Iterable[Supersession],
) -> tuple[bool, tuple[str, ...]]:
reasons: list[str] = []
if assertion.scope not in ("*", frame.scope):
reasons.append("scope mismatch")
if not assertion.valid.contains(frame.valid_at):
reasons.append("outside valid-time interval")
if assertion.recorded_at > frame.known_at:
reasons.append("not yet known at the knowledge cut-off")
edge = disqualifying_edge(assertion, frame, edges)
if edge:
reasons.append(f"{edge.kind} by {edge.successor}")
return (not reasons, tuple(reasons))
def select_one(
assertions: Iterable[Assertion],
proposition: str,
frame: DecisionFrame,
edges: Iterable[Supersession],
) -> Assertion:
candidates = [
item for item in assertions
if item.proposition == proposition
and admissible(item, frame, edges)[0]
]
if len(candidates) != 1:
ids = [item.id for item in candidates]
raise ValueError(
f"expected one admissible {proposition!r} assertion, found {ids}"
)
return candidates[0]
def evaluate_action(
request: ActionRequest,
assertions: list[Assertion],
edges: list[Supersession],
current_state_version: int,
) -> ActionResult:
reasons: list[str] = []
action_scope = f"{request.resource}:{request.action}"
action_frame = DecisionFrame(
valid_at=request.action_at,
known_at=request.action_at,
scope=action_scope,
)
grants = [
item for item in assertions
if item.proposition == "grant"
and item.value == f"{request.principal}|{request.action}|{request.purpose}"
and admissible(item, action_frame, edges)[0]
]
if not grants:
reasons.append("no grant is valid and unsuperseded at action time")
try:
policy = select_one(assertions, "governing_policy", action_frame, edges)
if policy.id != request.expected_policy_id:
reasons.append(
f"policy changed from {request.expected_policy_id} to {policy.id}"
)
except ValueError as exc:
reasons.append(str(exc))
if current_state_version != request.expected_state_version:
reasons.append(
f"world state changed from version "
f"{request.expected_state_version} to {current_state_version}"
)
return ActionResult(allowed=not reasons, reasons=tuple(reasons))
assertions = [
Assertion(
id="policy-v6",
proposition="governing_policy",
value="hold-rule-v6",
scope="payment:release",
authority="policy-registry",
valid=Interval(at("08:00:00"), at("11:00:00")),
recorded_at=at("07:45:00"),
source_version="sha256:v6",
),
Assertion(
id="policy-v7",
proposition="governing_policy",
value="hold-rule-v7",
scope="payment:release",
authority="policy-registry",
valid=Interval(at("11:00:00")),
recorded_at=at("09:30:00"),
source_version="sha256:v7",
),
Assertion(
id="candidate-1",
proposition="candidate_identity",
value="possible-match",
scope="payment:release",
authority="screening-feed",
valid=Interval(at("10:04:08")),
recorded_at=at("10:04:08"),
source_version="feed:8821",
),
Assertion(
id="candidate-2",
proposition="candidate_identity",
value="not-the-customer",
scope="payment:release",
authority="screening-feed",
valid=Interval(at("10:04:08")),
recorded_at=at("10:09:00"),
source_version="feed:8822",
),
Assertion(
id="grant-ops-17",
proposition="grant",
value="ops-17|release|alert-resolution",
scope="payment:release",
authority="access-control",
valid=Interval(at("09:00:00"), at("10:10:00")),
recorded_at=at("09:00:00"),
source_version="grant:417",
),
]
edges = [
Supersession(
predecessor="policy-v6",
successor="policy-v7",
kind="replaced",
scope="payment:release",
effective_from=at("11:00:00"),
recorded_at=at("09:30:00"),
),
Supersession(
predecessor="candidate-1",
successor="candidate-2",
kind="corrected",
scope="payment:release",
effective_from=at("10:04:08"),
recorded_at=at("10:09:00"),
),
]
historical = DecisionFrame(at("10:04:08"), at("10:04:08"), "payment:release")
current = DecisionFrame(at("10:12:00"), at("10:12:00"), "payment:release")
historical_policy = select_one(assertions, "governing_policy", historical, edges)
historical_candidate = select_one(assertions, "candidate_identity", historical, edges)
current_candidate = select_one(assertions, "candidate_identity", current, edges)
positive = evaluate_action(
ActionRequest(
principal="ops-17",
action="release",
resource="payment",
purpose="alert-resolution",
action_at=at("10:09:30"),
expected_state_version=14,
expected_policy_id="policy-v6",
),
assertions,
edges,
current_state_version=14,
)
negative = evaluate_action(
ActionRequest(
principal="ops-17",
action="release",
resource="payment",
purpose="alert-resolution",
action_at=at("10:12:00"),
expected_state_version=14,
expected_policy_id="policy-v6",
),
assertions,
edges,
current_state_version=15,
)
assert historical_policy.id == "policy-v6"
assert historical_candidate.id == "candidate-1"
assert current_candidate.id == "candidate-2"
assert positive.allowed
assert not negative.allowed
result = {
"historical_view": {
"policy": historical_policy.id,
"candidate": historical_candidate.value,
},
"current_view": {
"candidate": current_candidate.value,
},
"positive_action": {
"allowed": positive.allowed,
"reasons": list(positive.reasons),
},
"negative_action": {
"allowed": negative.allowed,
"reasons": list(negative.reasons),
},
}
print(json.dumps(result, indent=2))
{
"historical_view": {
"policy": "policy-v6",
"candidate": "possible-match"
},
"current_view": {
"candidate": "not-the-customer"
},
"positive_action": {
"allowed": true,
"reasons": []
},
"negative_action": {
"allowed": false,
"reasons": [
"no grant is valid and unsuperseded at action time",
"world state changed from version 14 to 15"
]
}
}The negative case is the important one. The historical statement “ops-17 held a release grant at 09:00” remains true. The programme denies the 10:12 release because the grant’s valid interval ended at 10:10 and the payment moved from state version 14 to 15. The denial is mechanical and inspectable; it does not depend on whether a language model notices the timing.
Part IV
stateDiagram-v2 [*] --> Claim state "The clocks hidden inside one<br/>“updated at” field" as Claim state "A clock is a question, not a<br/>column" as Test state "Minimal worked example, a fee<br/>waiver" as Revision Claim --> Test: expose mechanism Test --> Decision: evidence survives Test --> Revision: rival survives Revision --> Claim: narrow or rebuild Decision --> [*]
Test the timeline, then stop where the machinery stops helping
Evaluate transitions, not isolated answers
A conventional golden case supplies one prompt and one expected response. Temporal evaluation needs an event sequence and several cut-offs. The same question should produce controlled differences before and after correction, activation, suspension and revocation. Score the selected versions and dispositions before judging the prose.
A useful fixture mutates one feature at a time: deliver an earlier event late; publish a future-effective policy; correct a fact retroactively; suspend only one clause; revoke a grant between proposal and effect; duplicate an event; cross a half-open boundary; omit a time zone; expire a replay snapshot; or make an authoritative source unavailable. For each mutation, specify which historical view must remain stable, which current view must change and whether action must stop.
| Mutation | Invariant | Expected disposition | Failure signal |
|---|---|---|---|
| Late correction | Then-known explanation remains unchanged | Current view uses correction; impact review opens | History is silently rewritten or old claim stays current |
| Future policy | Predecessor governs until effective boundary | Select by scope and valid time | Search chooses the most recently published file |
| Grant revoked mid-task | Proposal receipt remains auditable | Effect denied and task recompiles | Cached approval executes |
| State version changes | No duplicate or cross-state effect | Optimistic conflict | Action commits against a state never evaluated |
| Source outage | Contract-specific stale rule remains visible | Revalidate, historical-only or escalate | Cache is served as current without qualification |
Metrics need temporal denominators. Report stale-use rate by proposition and decision type, correction-to-invalidation lag, supersession closure by scope, decision snapshot span, action precondition age and independent reperformance success. An average age across all fields can look excellent while one hard-stop source remains dangerously behind.
Properties worth proving
Example fixtures become more reusable when expressed as properties. Historical stability requires a query with fixed valid and knowledge cut-offs to return the same admitted set after later records arrive. Correction responsiveness requires the current frame to change when an applicable retroactive correction is recorded. Boundary exclusivity requires exactly one governing version at a clean handover. Scoped non-interference requires an amendment to leave unrelated scopes unchanged. Commit freshness requires any changed load-bearing state, grant or policy version to stop the effect.
Run each property against ordered delivery, delayed delivery, duplicate events and retries. Then reverse the insertion order while preserving event times. A correct evaluator should depend on declared temporal semantics, not arrival order or row identity. Property failures reveal whether the defect lies in ingestion, supersession closure, query compilation, policy evaluation or the action boundary, which is far more useful than a single end-to-end accuracy score.
The serious negative control
Temporal machinery is not automatically superior. Imagine a low-consequence service in which all relevant facts, policy and permissions live in one transactional database. A request reads the current row, evaluates a deterministic rule and commits the effect inside the same serialisable transaction. There is no delayed external source, no historical explanation requirement, no partial policy amendment and no long-running interval between check and use.
In that environment, a bitemporal assertion graph can add duplicate state, new reconciliation work and more ways to misconfigure scope. The simpler transaction already supplies current truth and closes the race. The preferred mechanism is unnecessary when one authoritative transactional boundary already contains the decision and effect. This negative control prevents “temporal entitlement” from becoming a platform fashion applied to every lookup.
The explicit failure boundary
Bitemporal evidence reconstructs stored belief, not reality itself. A snapshot can preserve a wrong source perfectly. A typed edge can encode the wrong scope. A signature can authenticate a mistaken policy. If history was never retained, the system cannot recover what was known. If two authorities genuinely disagree about when ownership changed, a precise interval would conceal rather than resolve the dispute.
The design also depends on complete lineage. An unregistered index, cache or spreadsheet can continue serving a corrected assertion. Current-only external APIs may prevent historical replay. Privacy and records rules may require deletion of source values, leaving only a constrained receipt or tombstone. Provider-managed models may not permit exact generative reproduction even when the evidence packet is fixed.
The mechanism breaks when temporal metadata is fabricated, source authority is untrustworthy, supersession coverage is incomplete, or the action service ignores preconditions. In those cases the correct response is an explicit unknown, quarantine, independent source check or human decision. More timestamps do not compensate for missing governance.
Decision instrument: the temporal entitlement release gate
Use the following gate for any agent step that can change external state, communicate a consequential claim or release a governed artefact. A “no” does not always mean permanent denial. It determines the next safe disposition.
Admit
The claim governs the frame, authority and state are current, and the action service can enforce the preconditions atomically.
Historical-only
The claim may explain what was known or valid then, but it cannot support a new effect or a present-tense assertion.
Revalidate
The contract permits a fresh read or introspection. Recompile the evidence packet and repeat policy evaluation before action.
Deny or escalate
Authority, scope, validity or state cannot be established. Preserve the unknown and route to an authorised decision-maker.
Roll out by decision risk, not corpus size. Start with one consequential transition whose events, source versions, policy releases and effects can be reconstructed. Instrument the existing human process. Build the version registry and receipts first, then valid and system time, typed supersession, lineage invalidation and action preconditions. Only after adversarial timeline tests pass should an agent receive bounded authority to propose the effect.
Compact glossary
- Valid time
- When a proposition applies in the represented world or policy domain.
- System time
- When a version was present in the institution’s recorded knowledge.
- Supersession
- A typed, scoped relation that changes which version may support which use.
- Temporal entitlement
- A claim’s eligibility to support a named decision or effect at a specified time.
- Decision receipt
- The immutable record of frame, evidence versions, policy, authority, proposal and limitations.
- Effect receipt
- The downstream proof of what state change committed, under which preconditions and outcome status.
Make time part of the permission
The dangerous stale fact is often neither false nor poorly sourced. It is a statement that outlived the conditions under which it could govern. That is why better prompting, a larger context window and a recency score cannot solve the problem. They improve selection or narration while leaving applicability implicit.
The architecture decision changes from “retrieve the latest evidence” to “compile the evidence and authority that are entitled to govern this frame, then recheck them at effect time”. Valid time separates world applicability from record age. System time preserves what was knowable. Typed supersession resolves replacement without erasing history. Per-proposition freshness contracts determine revalidation. Action preconditions bind the recommendation to current identity, purpose, policy and world state.
This design does not make a source true, settle contested timelines or justify indefinite retention. It makes temporal assumptions visible and falsifiable. A reviewer can see why an earlier action made sense, what later evidence changed, which version governs now and why a proposed effect was admitted or stopped.
The practical test is simple. Take one decision that can hurt someone if an old approval, corrected fact or retired policy survives too long. Build a timeline fixture in which every sentence remains true while one permission expires. If the system still acts, freshness is only a label. If it stops mechanically, records the conflict and recompiles from authoritative state, time has finally become part of permission.