# CHANGES REQUIRED — BLOCKERS REMAIN ## Blockers 1. **Cash does not use one authoritative ledger definition** - `modelled_wealth.py:75-88` includes **all** confirmed, non-void canonical transactions with a CHF amount. - `cash_service.py:182-190` includes only canonical transactions whose `source_type LIKE '%csv%'`. - Adversarial probe with a CHF 100 anchor and confirmed CHF 10 `vue_manual_cash` movement returned: - modelled wealth: **CHF 110** - cash position API: **CHF 100** - Thus the same account can display different balances in two Sprint 22 surfaces. The budget-vs-canonical double-count fix works locally, but there is still no shared authoritative cash-ledger contract. 2. **Aggregate bank-cash source date is not truthful for mixed-age accounts** - `modelled_wealth.py:625-648` reports the **maximum** source date across all bank accounts. - Probe with CHF 100 sourced on 2026-08-20 plus CHF 200 confirmed on 2026-08-26 reported the entire CHF 300 component `source_date/as_of = 2026-08-26`. - Although quality becomes `carried`, the displayed aggregate date implies all CHF 300 is evidenced on that date. This does not satisfy the true-source-date requirement. 3. **PostFinance model rows are not source-allowlisted** - `modelled_wealth.py:287-289` loads all qualifying portfolio valuation sources for PostFinance depot and settlement-cash accounts. - Probe rows with source `totally_unrelated_projection` were accepted and exposed as a CHF 1,000 PostFinance modelled value. - Official anchors are now fail-closed, but the modelled side can still ingest unrelated stored projections and corrupt the household total. ## Important 4. **Malformed official dates can crash the read model** - `_official_rows()` validates numeric finiteness but not `valuation_date`; `build_modelled_wealth_development()` later calls `date.fromisoformat()` at `modelled_wealth.py:542-547`. - A synthetically stored official row with `valuation_date='not-a-date'` caused `ValueError: Invalid isoformat string`, rather than being ignored/failing closed. - Malformed numeric official values are handled correctly; malformed date values are not. ## Verified fixes - Actual and legacy crypto sources are deduplicated per account/day rather than summed. - Mixed-date anchors retain truthful aggregate quality (`carried` in the probe, not falsely `confirmed`). - Missing expected investment components mark totals `incomplete`. - Official source and quality allowlists reject unrelated sources, bad quality, negative/non-finite or malformed numeric totals. - Budget and canonical cash projections are not added together inside the modelled read model. - Same-day model-before-official correction markers work. - Cash movement days can make the chart visible. - Unknown account labels are generic and did not expose names, account IDs, or IBAN-like text. - Carry-forward is forward-only; later valuations were not backfilled into earlier dates. - Modelled read calls were repeatable and did not change `conn.total_changes`. - PostFinance official totals replace, rather than add to, same-day depot-plus-cash models. - Daily valuation job idempotence test passed. - No provider calls or write side effects were found in the modelled GET path. ## Verification evidence - `37 passed` — modelled wealth, wealth cockpit, and cash/True Wealth suites. - `4 passed` — portfolio market analytics suite, including daily-job idempotence. - `6 passed` — focused blocker regression subset. - `git diff --check` passed. - Final `git status --short` matched the initial candidate tree. ## Files - **Created/modified by this review:** none. - `ruff` was unavailable in the active Python environment; no lint result was claimed.