# Modelled wealth read-model release

Use this reference when a finance cockpit must show estimated household wealth development from persisted anchors and daily valuations while strict XIRR/TTWROR coverage remains blocked.

## Keep three semantics separate

- **Confirmed anchor:** authoritative imported or manually confirmed provenance point.
- **Comparable baseline:** first point in the selected period with the same currently known major components and the same unknown-account scope.
- **Current modelled subtotal:** latest known/modelled value, with missing accounts explicit.

Do not compute the headline CHF/% change from the first partial point when components only become available later; that turns data onboarding into fictitious wealth gain. Prefer exact unknown-account-set identity for baseline comparison. A count is only a bounded fallback. If no prior comparable point exists, baseline equals current and change is zero while the full partial chart remains visible.

## Evidence-bearing account universe

An active bootstrap cash account is not automatically an economic unknown. Include it only when durable evidence exists: snapshot, confirmed transaction/balance row, active budget linkage, or active canonical source mapping. An empty account with none of these may be excluded without mutation. A mapped/linked account with no balance evidence remains unknown. Never classify placeholders by name, provider, suffix, or amount.

Counterprobe with one empty legacy placeholder and one mapped-but-unvalued account; only the latter appears in `unknown_accounts`.

## Aggregate quality versus point provenance

A household total can be `incomplete` because one account is unknown and still contain an exact confirmed anchor or exact modelled observation. Carry separate provenance flags such as `has_confirmed_anchor` and `has_modelled_value` for markers and accessible evidence text.

Do **not** use “some component has a confirmed anchor” to render the aggregate segment as solid. A mixed point may contain confirmed, carried, modelled, and unavailable components simultaneously. Render a household segment solid only when both aggregate endpoints are fully `confirmed`; render modelled, carried, incomplete, or mixed endpoints dashed. Counterprobe two adjacent days where different components are confirmed on each day while the remaining components are carried—the segment must stay dashed.

Quality remains source/day specific:

- `confirmed`: exact authoritative observation;
- `modelled`: exact persisted valuation;
- `carried`: older eligible observation reused forward only;
- `incomplete`: known subtotal with unknown required evidence;
- `unavailable`: no eligible evidence.

## Source composition

- Official same-day anchors outrank model rows.
- For a brokerage, choose either official total or depot plus settlement cash; never both.
- Use persisted exact-day valuations only. Current quotes never backfill historical days.
- Later confirmed anchors append correction markers and do not overwrite historical model points.
- Keep modelled display points out of XIRR/TTWROR inputs; strict flow/endpoint/FX gates remain unchanged.
- Source-allowlist model rows as well as official anchors. For example, a brokerage model may accept only its canonical daily-market source family; an unrelated stored projection must not enter the household total.
- Parse and validate every persisted observation date before period selection. The `all` path must derive its earliest date from the set of valid parsed dates, not raw SQL `MIN(day)`, because one malformed lexically early date can otherwise crash an otherwise valid read model.

## Shared cash movement contract

Cash position cards and the modelled-wealth reader must consume one shared movement resolver; duplicated SQL in two surfaces will drift.

For a canonical account with an active linked budget projection:

1. confirmed budget rows are authoritative for imported household activity;
2. known mirrored canonical import sources (CSV/bank import families) are excluded from the supplemental canonical set;
3. confirmed non-import canonical adjustments remain additive—for example, a user-confirmed manual cash correction;
4. without a budget projection, the full confirmed, non-void canonical ledger is used.

The required counterexample is an anchor of 100, a budget import of 10, a mirrored canonical CSV row of 10, and a canonical manual correction of 7. Effective cash evidence, the cash card, and the modelled bank component must all return **117**, never 110, 120, or 127.

When a bank subtotal combines accounts with different source dates, expose the oldest included component date as the conservative aggregate `as_of` and retain carried/incomplete quality. Reporting the newest date falsely implies every included amount is current to that day.

## Account-scoped metadata

Never clone institution-wide import counts onto every account. Attribute rows through confirmed canonical/budget lineage or private source-reference fingerprints. If historical duplicate/review scope cannot be reconstructed, mark it partial/unknown rather than reporting a fabricated account count.

## Release gates

1. Pin deployed commit/schema and prove production read-only preview leaves DB bytes/changes unchanged.
2. Validate the complete public response against the strict schema.
3. Probe two model days, mixed dates, one unknown account, placeholder exclusion, forward-only carry, correction markers, brokerage double counting, account-scoped metadata, and replay no-write behavior.
4. Run responsive laptop/iPad browser UAT with the actual route. Confirm technical performance blockers are collapsed and the model chart remains visible.
5. After every late patch—including a one-line account-universe or UI-fixture fix—rerun the complete backend/frontend suites and consume every independent review result. Earlier full-suite evidence is stale.
6. Run the exact CI-pinned tool versions from repository dependency metadata; do not substitute an unpinned latest linter whose default rules may differ.
7. Only then commit, push, open PR, consume remote CI, merge, deploy exact SHA, and repeat production GET-no-write/idempotence/UAT checks.
8. For deployed GET proof, issue the same endpoint twice, require byte-equivalent parsed payloads, and compare a frozen logical database digest plus per-table row counts before/after. A live SQLite file hash alone can change because of WAL/checkpoint metadata even when the read model writes nothing.
9. Reconcile the headline total against the exact component sum, the brokerage component against the official total anchor, and the bank component against account-level effective cash evidence. This is the productive double-counting sentinel.
10. Browser UAT must inspect the deployed route, not a pre-merge dev server: capture target viewports, assert no horizontal overflow, measure 44px controls, count dashed chart segments/components, verify collapsed performance/diagnostics/accessibility details, check failed resources and runtime exceptions, then remove private screenshots and isolated test servers after extracting aggregate evidence.

## Common regression traps

- Page-level tests mock an older cockpit payload, so the new component renders only its header or throws on missing nested arrays. Update every page/smoke fixture, not only the component test.
- Aggregate `incomplete` hides confirmed chart points when segment rendering keys only on total quality.
- A generic empty seed account creates a second fake unknown account.
- A selected one-month period shows a huge return because newly covered components are compared against an earlier partial subtotal.
- Full suites were green before the last corrective patch and are reported as final evidence.
- A linked budget projection causes the reader to choose budget **or** canonical movements, suppressing valid manual canonical corrections; use the shared hybrid resolver and exact 100+10+duplicate10+7 probe.
- Official anchors are source-allowlisted, but model rows accept any stored projection under the account and silently corrupt the subtotal.
- Per-row date validation exists, but `period=all` still parses raw `MIN(day)` and crashes on one malformed lexically early date.
- “Some confirmed component at each endpoint” renders a mixed/carried household segment solid even though the total is not fully confirmed.
- A pre-merge or isolated-port screenshot is reported as productive UAT. Re-run against the deployed exact merge SHA, prove route/resources/overflow/details, and clean private captures afterwards.
