# Historical cache fallback, provenance, and release closeout

Use this pattern when a historical valuation provider transiently fails but a previously audited quote exists for the requested cutoff.

## Exact-date fallback contract

A cached quote is eligible only when all of these match:

- canonical instrument ID;
- requested effective business date exactly (`price_date == cutoff`);
- provider and provider symbol;
- expected trading currency;
- positive close value;
- `quality_status = fresh`;
- no stored error.

Attempt the live provider first. Use the cache only after a missing/invalid provider result. Reject future dates and do not widen the fallback window implicitly.

## Immutable provenance

- Never assign an existing market-price or FX row to the current run.
- If the same date/provider row already exists with the same semantic value, leave it untouched.
- If it differs in close/rate, currency, or symbol, use the provider-correction workflow or fail closed; never silently upsert over it.
- Record the cached row's stable ID, original run ID (including explicit null for legacy data), created/fetched timestamps, instrument, provider, symbol, currency, and cutoff in the analysis or audit payload.
- A generic reason code such as `cached_exact_date_price` is not sufficient lineage by itself.

## Algorithm/source versioning

When cache eligibility, quality calculation, lineage, or metric semantics change, bump the analysis source key. Keep old runs and snapshots immutable. A rerun under the new source key creates a separately auditable run; its immediate replay must return the same run ID with `idempotent=true`.

## Separate availability, coverage, and writes

These are different facts and must not share one counter:

- `inputs_accepted`: valid live plus eligible cached observations used by the run;
- `coverage_pct`: eligible positions actually valued with current-enough price/FX inputs;
- `rows_written`: database rows newly persisted by this run.

Legacy fields named `*_stored` should contain actual persistence writes, and CLI labels should say `price_writes`/`fx_writes`. Put accepted-input counts and coverage in analysis/audit fields. A cached input can increase accepted inputs and coverage while leaving rows written at zero.

Also count persistence by unique row identity rather than account-position count: the same instrument held in two accounts writes one price row but can produce two valuation snapshots.

## Regression probes

1. Exact fresh cached quote recovers a transient provider gap.
2. Stale or error-marked cached quote remains missing and cannot yield complete coverage.
3. Cached row ID, value, currency, symbol, provider, and original run ID remain unchanged.
4. Existing same-date live quotes and FX rows retain their earlier run IDs.
5. Analysis/audit identifies the precise cached row.
6. Accepted-input, coverage, and rows-written metrics diverge correctly.
7. Future quotes and FX rates are absent from the valuation payload.
8. Immediate replay is idempotent and writes no additional rows.

## Production closeout

- Stop the scheduler before deploying a source-version change or running a controlled historical valuation.
- Take a new integrity-checked backup immediately before the mutation, even if a broader sprint backup already exists.
- Capture pre-run protected-table digests and exact market/FX row payloads.
- Run once, replay once, then prove protected digests and existing market/FX rows are unchanged.
- Restart affected services and restore the timer with its intended enabled/active and persistent settings.
- Record both the next scheduled local-time run and the last trigger/result; do not describe a merely scheduled future run as successful.
