Data Integrity & Model Governance
How Philidor enforces evidence quality, approvals, provenance, and fail-safe behavior in v4.1.
Risk scores are only as reliable as their inputs and controls. v4.1 adds model-governance guarantees so score changes are traceable, reproducible, and reviewable.
1) Identity And Bitemporal Records
- Assets are keyed by
address + chain_id(symbol is display-only). - Classification facts are versioned with:
valid_from/valid_to(when true in the real world)recorded_at(when recorded in Philidor)
- Point-in-time queries can answer: "What did we publish as of date X using information known at that time?"
2) Maker-Checker Controls (4-Eyes)
Score-affecting changes require separation of duties:
- preparer and approver must be different users
- approvals are tracked with timestamps and change payloads
- manual hard-fail flags and overrides follow explicit approval flow
3) Evidence And Freshness
Each dimension value is backed by observations with source, observed_at, fresh_until, and confidence.
Freshness handling:
- stale evidence: value haircut
- expired evidence: conservative fallback
- high stale share: global staleness cap
Approved classifications are linked to the exact evidence used (lineage records).
4) Event-Sourced Flags And Overrides
- hard-fail flags are stored as events (
triggered,cleared,cooldown) - incident overrides require expiry and approval controls
- when multiple caps apply, the most conservative cap binds
- sanctions exposure remains manual-verified in current scope
5) Score Run Provenance
Every scoring batch is recorded with:
- methodology version
- run identifier
- input snapshot hash
- fail-safe mode at run time
This supports deterministic replay and post-incident audit.
6) Operational Fail-Safe
Fail-safe states:
normal: standard scoringdegraded: serve last-known-good completed run with explicit banner/headerfail_closed: return no score output (API503) when critical dependencies are unavailable
This avoids silent degradation and "best guess" scoring under broken dependencies.
7) Chain Data Integrity
Philidor still enforces on-chain ingestion protections: finality depth, reorg-safe cursor handling, idempotent event writes, and reconciliation checks.
Per-chain confirmation depths prevent reading unfinalized (potentially reorganized) data:
| Chain | Chain ID | Confirmation Depth | Notes |
|---|---|---|---|
| Ethereum | 1 | 64 blocks | 2 epochs post-Merge |
| Polygon | 137 | 128 blocks | Heimdall checkpoints |
| Avalanche | 43114 | 1 block | Sub-second finality |
| Optimism | 10 | 0 | L2 sequencer, finality via L1 |
| Arbitrum | 42161 | 0 | L2 sequencer, finality via L1 |
| Base | 8453 | 0 | L2 sequencer, finality via L1 |
8) Reorg Safety
Six invariants protect against data corruption from chain reorganizations:
- Finalized blocks only —
toBlockcapped atgetFinalizedBlockNumber, nevergetBlockNumber - Persist-then-advance — events written to database before sync cursor advances. If persistence fails, cursor stays put
- All-or-nothing — any batch/module failure means no state advancement. Retry next cycle
- Reorg rollback never jumps forward — uses
Math.min(lastFinalizedBlock, lastProcessedBlock) - DB write failures propagate — RPC read failures are swallowed; DB write failures are exceptions
- Deduplication —
ON CONFLICT (tx_hash, log_index) DO NOTHING. Re-fetching is safe
9) Reconciliation
Automated checks run every 30 minutes to detect:
- Sync gaps — missing block ranges in the event log
- Event density drift — unusual changes in event frequency
- Stale cursors — sync state that hasn't advanced
- Data anomalies — inconsistencies between expected and observed data
Anomalies trigger alerts before they affect risk scores.
10) Sync Intervals
| Process | Interval | Description |
|---|---|---|
| API sync | 30 minutes | Pull from protocol APIs |
| On-chain indexer | 30 minutes | Poll RPCs for events |
| Risk calculator | 30 minutes | Recompute all risk scores |
| Oracle freshness | 15 seconds | Poll Chainlink feeds |
| Reconciliation | 30 minutes | Verify data consistency |
11) Oracle Freshness Monitoring
A dedicated worker polls Chainlink price feeds every 15 seconds and writes freshness data to Redis. This provides:
- Real-time staleness detection for oracle-dependent vaults
- Historical freshness data for the Oracle Freshness API
- Automatic score adjustment when feeds go stale
12) APR Data Sources
APR is sourced from on-chain share price reads (ERC-4626) or protocol APIs (Aave, Compound). Forward-looking estimates and boosted marketing figures are never used.
- Base APR: native protocol return (lending interest, share price accrual)
- Rewards: token emissions, points, trading fees — sourced from Morpho rewards API, Aavescan, and protocol subgraphs
- Invariant:
apr_net = base_apr + SUM(reward APRs)