A production decision at 2026-08-18 14:00 used MissedPayments_90d = 2. Today, the offline warehouse says the historical value was 1. Was the difference caused by a late payment, a correction, new feature logic, a window boundary, stale online state or a changed identity mapping?
If the platform cannot answer why, it is not a controlled model-input layer. Serving matching column names online and offline is not enough.
A feature store is a semantic contract, not a scalar cache
type FeatureDefinition = {
featureName: string;
entityType: "PARTY" | "FACILITY" | "ACCOUNT";
valueType: "NUMBER" | "BOOLEAN" | "CATEGORY";
definitionVersion: string;
observationWindow?: string;
sourceDomains: string[];
};Attach a feature to its economic object: payment volatility may be account-level, utilisation facility-level and total internal exposure party-level. Putting every feature under customer_id hides aggregation and duplication errors.
For PaymentRatio_90d, define the exact interval—such as (T−90d, T]—and which timestamp admits an event. Event, effective and posting time are not interchangeable SQL conveniences. Boundary conventions belong in the contract and in tests.
Effective, available and calculated time answer different questions
type FeatureValue<T> = {
entityId: string;
featureName: string;
value: T;
effectiveAsOf: Date;
availableAsOf: Date;
calculatedAt: Date;
definitionVersion: string;
};One definition feeds two serving layers with different workloads
low-latency current servingOFFLINE PIT STORE
training · validation · replay
The physical code path may differ, but event inclusion, null handling, identity mapping, reversals and window boundaries may not. An online store alone has no historical modelling capability; an offline store without availability time creates hindsight leakage.
Point-in-time retrieval chooses the latest value legitimately available
getFeatureAsKnown(
entityId,
featureName,
decisionTime
) // latest value with availableAsOf <= decisionTimeA restated API answers a different question: today's corrected feature for an historical effective time. Keep getFeatureAsKnown and getRestatedFeature separate.
SELECT d.decision_id, d.party_id, f.value
FROM decisions d
LEFT JOIN LATERAL (
SELECT value
FROM feature_history f
WHERE f.party_id = d.party_id
AND f.feature_name = 'payment_ratio_90d'
AND f.available_as_of <= d.decision_time
ORDER BY f.available_as_of DESC
LIMIT 1
) f ON TRUE;SQL dialects differ. The invariant does not: joining current features is wrong, and filtering only effective_as_of still admits a value computed later. An immutable feature_snapshot_id can simplify replay; dynamic retrieval lowers duplication but demands an immutable temporal store. A hybrid preserves critical scalars, a snapshot reference and reconstructible history.
The registry turns feature names into governed contracts
type FeatureRegistryEntry = {
name: string;
entityType: string;
definitionVersion: string;
owner: string;
sourceDependencies: string[];
observationWindow?: string;
online: boolean;
offline: boolean;
};| Dimension | Required meaning |
|---|---|
| Business definition | What economic behaviour does the value represent? |
| Entity grain | Party, facility, account or explicit context |
| Type and range | Representation and expected domain |
| Null semantics | Unavailable, not applicable, missing source or failed computation |
| Time semantics | Window boundaries, membership timestamp and freshness |
| Sources | Exact governed upstream domains and semantic fields |
| Owner | Definition, change control and quality accountability |
| Serving mode | Online, offline or both |
NULL → 0 can silently change meaning. Missingness flags are valid only when online and offline computation preserve identical missing semantics.
Definition changes create new feature versions
Changing utilisation_30d from average drawn/limit to maximum drawn/limit is a new definition even if the label stays. A pure rename may not be. Never overwrite v1 history with v2 logic.
{
"modelVersion": "pd-v5",
"features": {
"payment_ratio_90d": "v3",
"max_dpd_180d": "v2"
}
}The model registry validates exact feature contracts before deployment. If the model expects v3 and only v4 is available, do not silently substitute: fail validation or take a versioned governed fallback.
Freshness is feature- and decision-specific
A decision policy defines FeatureAge ≤ FreshnessBudget(D,X). Current utilisation may require high freshness; annual income naturally moves slower. One global SLA is semantically weak.
Real time is not inherently superior. A mixed vector is sound when each component exposes its own freshness and the decision accepts it explicitly.
Incremental features must equal full recomputation
A streaming PaymentCount_90d can maintain a queue and rolling count rather than scan every customer. Its state remains a performance optimisation, not the source of methodology.
Compute on write, read or a schedule according to cost, latency, volume and reproducibility. Compute-on-read without a pinned definition can make an old decision return a newly defined feature. Historical values used by decisions should remain immutable by definition version and state mode.
Late events create known and restated feature history
The exact value available to production. Immutable for decision replay.
The corrected value after late payments, reversals or identity changes.
A late payment may alter today's feature and restated history while leaving the actual historical decision input unchanged. Distinguish production replay backfill, which reconstructs known values, from restated analytical backfill, which uses corrected knowledge.
Every backfill records feature version, source-state mode and execution time. It must never rewrite an immutable decision snapshot.
Feature lineage is a dependency graph, not a free-text note
getFeatureTrace(
entityId,
featureName,
asOf
) // definition, inputs, state refs and timestampsLineage includes source event/state, transformation version, identity-resolution version and calculation time. It need not inflate every online response, but it must be queryable for model validation and incidents. A derived feature such as current PD minus 30-day-ago PD must preserve both dependency versions.
Point-in-time controls block four distinct leakage paths
| Class | Failure | Control |
|---|---|---|
| Future event leakage | Feature includes an event after the decision | Window ends at decision time |
| Availability leakage | Earlier-effective event became known later | Require availableAsOf ≤ decision time |
| Population leakage | Future identity or group relation enters history | Use identity/relationship version known then |
| Target leakage | Outcome information enters pre-decision inputs | Enforce source and temporal contract |
| Window leakage | 10:00 decision receives full 23:59 day aggregate | Use exact intraday boundary, not end-of-day shortcut |
The serving API returns values with versions and time
interface FeatureService {
getCurrent(
entityId: string,
featureSet: string[]
): Promise<FeatureVector>;
getAsKnown(
entityId: string,
featureSet: string[],
asOf: Date
): Promise<FeatureVector>;
}
type FeatureVector = {
values: Record<string, unknown>;
featureVersions: Record<string, string>;
effectiveAsOf: Record<string, Date>;
availableAsOf: Record<string, Date>;
};The decision manifest references the feature-set version, exact feature versions and immutable snapshot or history references. A training dataset manifest adds extraction time, entity population, observation period, point-in-time mode and source versions; a fingerprint can verify metadata and rows, but never replaces lineage.
Shadow comparison turns feature changes into decision evidence
Before replacing an online computation path, run old and new versions over the same live traffic without changing decisions. Compare disagreement rate, magnitude, distribution and downstream decision impact.
Stable distributions are not proof of semantic consistency: payment count may still average three after its source silently changes from settled to initiated payments. That is semantic drift—economic meaning changed while name and schema remained stable.
A golden feature stream tests time, identity and computation together
| Test | Expected proof |
|---|---|
| Window boundaries | Events exactly at each boundary follow the declared convention |
| Point-in-time join | No value with availability after T enters training or replay |
| Incremental equality | Incremental value equals full recomputation for identical history |
| Duplicate event | Idempotent ingestion leaves the feature unchanged |
| Late payment | Restated history changes; known historical input does not |
| Reversal | Payment features follow the versioned economic-event semantics |
| Identity correction | Current mapping never leaks into old party features |
| Version compatibility | A model cannot consume an undeclared feature version |
| Missing feature | Unavailable, failed and not-applicable remain distinguishable |
| Online rebuild | Rebuilt serving state equals canonical feature history |
Use deterministic streams covering clean payments, missed dues, reversal, late payment, multiple facilities, stale source and identity correction. Rebuild both online state and offline datasets from governed definitions rather than treating ad hoc extracts as methodology truth.
Monitor semantics and lineage as well as distributions
| Incident | Question |
|---|---|
| Availability | Is the feature missing or stale? |
| Semantic | Did definition or source meaning change? |
| Computation | Did the logic fail or diverge? |
| Temporal | Was point-in-time correctness violated? |
| Identity | Was the feature attached to the wrong economic entity? |
No universal thresholds apply. End-to-end freshness catches a healthy cache serving stale state; upstream semantic contracts catch pipelines that succeed technically while changing economic meaning.
The Entimema architecture makes time part of every feature value
training / validationONLINE SERVING STORE
decision engine
- Define feature semantics
- Define entity grain
- Define observation window
- Define time semantics
- Define version
- Compute
- Persist immutable history
- Serve online
- Retrieve point-in-time offline
- Compare and monitor
A Feature Integrity & Point-in-Time Agent can investigate discrepancies without changing production
A controlled agent can monitor definitions and versions, compare online and offline values, identify staleness and availability leakage, trace lineage, detect model/feature incompatibility and upstream semantic drift, compare incremental with full recomputation and find historical decisions affected by defects.
Credit Risk
Govern feature definitions, validation datasets and model compatibility.
Decision Automation
Serve versioned, freshness-aware feature vectors to decisions.
Financial Data
Preserve event, state, time and identity lineage beneath every feature.
Continue with Point-in-Time Customer State Reconstruction, Handling Joint Borrowers and Connected Exposures, Building a Golden Customer Record, Event Time vs Processing Time vs Posting Time, Building a Reliable DPD Engine, Behavioural Credit Scoring and Early Warning Systems. Streaming behavioural features, real-time utilisation and silent-schema-change detection remain future research directions—not fabricated routes.