A payment occurs at 09:15. The platform receives it at 09:17. The loan system posts it at 23:45. The risk warehouse receives that posting at 01:30 the next day. Collections made a decision at 18:00. Was the borrower delinquent at 18:00?
Economically, perhaps not: the payment was already effective. Operationally, perhaps yes: the collections engine may not have possessed a validated payment state. Accounting may answer differently again until formal posting. The defect is not that one answer must be chosen. It is that a schema such as payment_date TIMESTAMP destroys the evidence needed to answer each question.
One timestamp cannot represent six questions
interface TemporalFinancialEvent {
eventId: string;
eventTime: Instant;
effectiveTime: Instant;
receivedTime: Instant;
processedTime: Instant;
postingTime?: Instant;
sourceSystem: string;
}| Timestamp | Symbol | Question | Engineering meaning |
|---|---|---|---|
| Event time | Tₑ | When did it happen? | Source assertion about the real-world event |
| Effective time | Tᵥ | When should it affect economic state? | Valid time for the domain being modelled |
| Received time | Tᵣ | When did we learn about it? | First arrival at the controlled platform boundary |
| Processing time | Tₚ | When was it technically ready? | Canonical validation and transformation completed |
| Posting time | Tpost | When was it formally recorded? | Servicing or accounting system posted the item |
| Decision time | Tᵈ | When did we act? | Immutable time of approval, alert, limit or collections action |
Event time may be initiation, utilisation change or an external bureau occurrence. Effective time is when the fact becomes valid for balance, exposure or delinquency and need not equal event time. Received time establishes institutional knowledge. Processing time measures infrastructure readiness. Posting time is a formal system-of-record fact, not a synonym for payment or economic validity. Decision time freezes the information boundary for reproducibility.
Credit architecture contains two histories
The availability predicate normally includes receivedTime ≤ T and may additionally require successful processing, source eligibility or finality appropriate to the decision. This reconstructs what production could have known.
Restated state uses later arrivals and corrections to express today's best view of what was economically true at T. It is appropriate for reconciliation and corrected portfolio history, but it is not a faithful decision input.
If a Monday payment arrives Wednesday and a model scored Tuesday, training or validation built from Wednesday's corrected history gives the model information production lacked. That is hindsight leakage.
Effective before the decision does not mean available before the decision
The dangerous query is attractive because it looks temporal:
SELECT *
FROM payments
WHERE effective_time <= :decision_time;It admits a fact effective on Monday even when the platform first received it on Wednesday. A conceptual known-state filter begins with both axes:
SELECT *
FROM financial_events
WHERE effective_time <= :decision_time
AND received_time <= :decision_time
AND processed_time <= :decision_time
AND processing_status = 'ACCEPTED';Production semantics may also constrain source availability, version, finality and corrections. The SQL must implement the decision's availability contract, not a universal folklore rule.
As-of joins select what was available then
SELECT d.decision_id, d.decision_time, s.balance_minor
FROM decisions d
LEFT JOIN LATERAL (
SELECT s.balance_minor
FROM account_state_history s
WHERE s.account_id = d.account_id
AND s.available_from <= d.decision_time
AND (s.available_to > d.decision_time OR s.available_to IS NULL)
ORDER BY s.available_from DESC, s.state_version DESC
LIMIT 1
) s ON TRUE;LATERAL is supported by PostgreSQL and some related engines; other dialects use APPLY, a correlated subquery or ROW_NUMBER(). The invariant is stable: never join a historical decision to today's customer row.
SCD Type 2 preserves one history; some decisions need two
A conventional slowly changing dimension can preserve when an attribute was valid yet discard when the institution learned or corrected it. Bitemporal data keeps both valid time and system time: Fact(validTime, systemTime).
CREATE TABLE account_state (
account_id TEXT NOT NULL,
balance_minor BIGINT NOT NULL,
valid_from TIMESTAMPTZ NOT NULL,
valid_to TIMESTAMPTZ,
system_from TIMESTAMPTZ NOT NULL,
system_to TIMESTAMPTZ,
state_version TEXT NOT NULL,
CHECK (valid_to IS NULL OR valid_from < valid_to),
CHECK (system_to IS NULL OR system_from < system_to)
);A payment effective Monday and received Wednesday has valid_from = Monday and system_from = Wednesday. Tuesday's known state excludes it; today's restated view of Tuesday includes it. A current row has an open system interval; correction closes that interval and inserts a new system version rather than erasing evidence.
Late events are a controlled state transition, not an edge case
| Class | Cause | Response |
|---|---|---|
| Expected late | Known source cadence | Encode availability contract; do not mislabel as incident |
| Operational delay | Backlog or failed dependency | Recover pipeline and measure affected decisions |
| Correction | New fact supersedes old state | Version state, restate dependencies, retain lineage |
| Unexpected late | Source or semantic failure | Quarantine or investigate according to materiality |
A stream watermark means events before T are believed sufficiently complete for a computation. It is an engineering completeness assumption—not payment settlement or financial finality. Allowed lateness may reopen a window, but reopening propagates through dependencies:
- Late payment
- Balance
- DPD
- Behavioural features
- PD
- Monitoring
Recalculate the affected account, aggregate and descendants rather than rebuilding the entire portfolio. The dependency graph should make the blast radius explicit.
Restating state must not rewrite what the institution actually decided
A historical production decision is itself immutable evidence. Store decision time, output, reason codes, input references, model, rule, strategy and configuration versions. A correction may produce a separate counterfactual—never a silent overwrite.
What production produced from contemporaneously available information.
What the same governed logic would produce with corrected information.
A limit increase issued Tuesday can remain the actual event even if a bureau item received Wednesday but effective Monday would have changed the answer to “no increase.” The difference measures data-incident impact, challenger opportunity or control weakness; it does not alter history.
{
"decisionId": "dec_102",
"decisionTime": "2026-08-18T18:00:00Z",
"strategyVersion": "limit_7.3",
"features": {
"behaviouralPd": {
"version": "pd_4.2",
"effectiveAsOf": "2026-08-18T06:00:00Z",
"availableAsOf": "2026-08-18T06:08:14Z"
}
}
}A point-in-time feature store answers what was available at T
interface FeatureValue<T> {
value: T;
effectiveAsOf: Date;
availableAsOf: Date;
calculatedAt: Date;
definitionVersion: string;
}calculatedAt is not freshness: a feature computed one second ago can use week-old data. effectiveAsOf describes economic age; availableAsOf describes when it could be served.
Online and offline stores need shared semantic definitions. Identical feature code still creates training-serving skew when offline training uses corrected history while online scoring saw delayed information. Availability-aware training is mandatory when the objective is production replication.
For PaymentsLast90Days, define the clock (event or effective time), exact inclusive/exclusive boundaries, timezone and availability cutoff. At 2026-08-18 12:00, a payment exactly 90 days earlier at 14:00 lies outside a duration-based window even if both dates look included after truncation.
Business dates and instants answer different questions
| Concern | Rule | Failure prevented |
|---|---|---|
| Time zones | Store UTC instants where appropriate and retain source offset/zone | Mixed-zone ordering errors |
| DST | Never store ambiguous local wall time without zone | Duplicated or missing local times |
| Business date | Preserve alongside physical timestamp | After-midnight posting assigned to wrong operating day |
| Calendar logic | Use contractual holiday and day-count rules | Incorrect DPD or schedule state |
| Precision | Match source and ordering need; do not invent digits | False deterministic ordering |
| Clock skew | Compare source and platform clocks | Impossible negative or extreme latency hidden |
DATE(event_time) discards ordering and zone information; use it only when the domain definition genuinely operates at date granularity. A transaction processed after midnight may belong to yesterday's business date. DPD should not be inferred from raw elapsed hours when contracts operate on calendar and holiday rules.
Future-dated input requires classification. It may be a timezone defect, source-clock error, or a legitimate future-effective rate or limit change. The institution can know today that a change becomes valid tomorrow; known now / effective later is a valid state, not necessarily an anomaly.
Make the temporal question explicit in the API
interface TemporalStateStore {
getKnownState(accountId: string, asOf: Date): Promise<AccountState>;
getRestatedState(accountId: string, asOf: Date): Promise<AccountState>;
}
const decisionState = await stateStore.getKnownState(
"acc_9012", new Date("2026-08-18T18:00:00Z")
);
const financeState = await stateStore.getRestatedState(
"acc_9012", new Date("2026-08-18T18:00:00Z")
);Known-state APIs support historical decision reconstruction, incident analysis and model validation. Restated-state APIs support Finance/Risk reconciliation and corrected portfolio analysis. Every exported dataset should declare which state it contains; a generic getState silently invites misuse.
type TemporalEvent = {
eventTime: Date;
effectiveTime: Date;
receivedTime: Date;
processedTime?: Date;
};
function validateTemporalEvent(event: TemporalEvent): string[] {
const errors: string[] = [];
if (event.processedTime && event.processedTime < event.receivedTime) {
errors.push("processedTime precedes receivedTime");
}
return errors;
}Keep generic invariants narrow. receivedTime ≥ eventTime often holds, but source clock skew can violate it without proving the business event invalid. Domain validators should classify unexpected negative latency, future times, overlaps and duplicates with source-aware tolerances.
There is no universal financial-state freshness requirement
| Decision | State requirement | Primary concern |
|---|---|---|
| Collections | Fresh known state | Avoid stale or harmful action |
| Behavioural scoring | Point-in-time known features | Hindsight leakage |
| Limit decision | Current exposure state | New exposure creation |
| ECL | Complete reporting-date state | Cut-off and reproducibility |
| Model validation | Historical known state | Credible performance estimate |
Collections may require current payment, DPD and promise state. Limit management may combine fast utilisation with slower income or bureau facts. Monthly ECL values completeness and a controlled reporting cut-off more than milliseconds. Mixed cadence is legitimate when explicit, tested and visible.
Temporal integrity is business observability
Monitor completeness, freshness, ordering, late arrival and temporal consistency. Use median, p95 and p99 rather than average alone: a small tail can concentrate in high-risk products, material exposures or one source. Slice by source, event type, product and time of day.
Do not alert on every late row. Alert on persistent source degradation, abnormal tails or material decision impact. Compare known and restated decisions where feasible; one decision-changing late event can matter more than thousands of harmless delays.
A late payment creates two valid Tuesday states
A fictional lender receives a payment on Wednesday at 07:10 that was effective Monday at 09:15. Its behavioural score and collections queue ran Tuesday at 18:00.
| View at Tuesday 18:00 | State | Decision |
|---|---|---|
| Production-known | Payment unavailable; account delinquent | CONTACT |
| Restated economic | Payment effective Monday; account current | NO CONTACT |
The CONTACT event remains immutable because that is what the institution did. Model backtesting uses known state because it reproduces the available evidence. Finance may use restated state to reconcile economic history. Infrastructure monitoring classifies the payment as decision-material late data because the counterfactual changed.
Temporal correctness needs deterministic regression evidence
A golden temporal stream should contain a normal payment, late-arriving payment, reversal and future-effective limit change. Assert known and restated state at several exact instants—not only end-of-day snapshots.
| Test | Assertion |
|---|---|
| On-time event | Known and restated state converge after processing |
| Late event | Historical known state excludes; restated state includes |
| Backdated correction | Old system interval closes; valid history restates |
| Future-effective event | Known schedule exists; current valid state is unchanged |
| Duplicate | Idempotent ingestion does not double-apply |
| Reversal | Original evidence remains; derived state reverses once |
| Timezone / DST boundary | Instant ordering remains unambiguous |
| Business-date boundary | Contractual date follows approved calendar rule |
Also test non-overlapping valid/system ranges where exclusivity is required, duplicate effective states, impossible system ranges, clock anomalies and date-boundary inclusivity. A golden decision replay stores expected known state, feature manifest and output for representative historical decision times and runs after every temporal-engine change.
Eighteen failure modes that corrupt historical state
Collapses incompatible business, availability and accounting meanings.
Consumers invent semantics independently.
Introduces later knowledge into earlier decisions.
Inflates historical model evidence through hindsight.
Applies future attributes to past decisions.
Often retains validity but not knowledge history.
Adds write, query and control complexity without decision value.
Makes cross-zone ordering ambiguous.
Breaks cut-off, holiday and end-of-day semantics.
Destroys ordering and window boundaries.
Leaves balances, DPD and features silently wrong.
Erases evidence of actual institutional action.
Hides stale source state behind a recent computation.
Conceals decision-material tail events.
Confuses compute completeness with economic settlement.
Creates temporal training-serving skew.
Makes exact replay impossible.
Allows boundary and correction bugs to recur.
A Temporal Integrity Agent can support evidence, not rewrite history
A future agent can monitor event, effective, received and processing timestamps; detect late or impossible sequences; measure latency distributions; compare known and restated state; identify affected historical decisions; test point-in-time feature integrity; surface potential hindsight leakage; and prepare incident evidence for engineers and validators.
Credit Risk
Point-in-time modelling, behavioural scoring, validation and ECL state.
Finance
Restated reporting state, reconciliation and controlled cut-offs.
Decision Automation
Freshness-aware decisions, immutable manifests and governed replay.
Related live research: The Payment Is Not the Balance, The Hidden Infrastructure Debt of Modern Lending, Why Batch Risk Is Becoming a Business Risk, Credit Risk Model Validation Pipeline and Decision Engine Monitoring. Future Engineering work can extend this foundation into idempotency, event-sourced account reconstruction, corrections, reversals, point-in-time feature stores and DPD engines; these are research directions, not fabricated routes.