## P0 - None. ## P1 1. **PostFinance activation succeeds with an incomplete two-account scope** — `src/jarvis_finance/services/performance_activation.py:131-160` Activation only requires `account_ids` to be non-empty. Although the component preview reports missing roles, that result is never converted into a blocker. A probe with only `postfinance_etrading_depot` included returned `missing_roles=['postfinance_etrading_cash']`, `blockers=[]`, and `can_confirm=True`. This permits activation without settlement cash and breaks the required component boundary. **Suggested fix:** Require exactly one included account for each PostFinance role and require component provenance/reconciliation to be ready before setting `can_confirm=True`. 2. **Source priority selects partial daily valuations over complete official components** — `src/jarvis_finance/services/portfolio_performance.py:528-555` A source becomes eligible merely by containing both account IDs; quality status is ignored. Since `daily_market_fx*` always outranks official components, a partial/stale daily pair replaces a complete official pair. A probe selected the partial daily source and produced `Quality(status='partial', reasons=('stale_valuation',))` despite complete official components being available. **Suggested fix:** Filter candidates to complete, base-convertible values for every expected account before applying source priority. Fall back to the highest-priority complete source; do not let an incomplete source suppress a complete one. 3. **Unpaired transfers remain internal in the production performance loader** — `src/jarvis_finance/services/portfolio_performance.py:343-353` `fully_internal` checks only whether the observed account set is within scope. A one-leg group therefore remains `internal_transfer`, while the new reclassification preview correctly labels the same case unclear. A runtime probe returned `('solo', 'internal_transfer', True)`, so an active performance calculation can silently omit a real boundary cashflow. **Suggested fix:** Share one transfer-boundary classifier between preview and calculation. Require a valid paired group before treating it as internal; otherwise mark it unsupported/unclear and degrade performance quality. 4. **A new confirmation ID can insert the same True Wealth cashflow again** — `src/jarvis_finance/services/performance_hardening.py:519-561, 623-656` Existing period rows are fingerprinted but never reconciled with requested entries. Transaction identity includes `confirmation_id`, so replaying the identical flow under another confirmation ID creates a second confirmed transaction and double-counts external cashflow. A probe produced two CHF 100 rows for the same dated/evidenced flow. **Suggested fix:** Derive a stable business identity independent of `confirmation_id`—account, date, direction, currency, amount, CHF amount, and evidence—or explicitly reconcile against existing rows during preview. Return zero planned writes/idempotent for exact matches and reject conflicts. 5. **Confirming a narrower True Wealth period destroys broader coverage** — `src/jarvis_finance/services/performance_hardening.py:665-678` Confirmation calls the account-keyed coverage upsert, which replaces the existing interval. A probe first confirmed `2026-06-30..2026-07-27`, then confirmed `2026-07-20..2026-07-27`; the stored coverage became only the narrower interval. Previously valid TTWROR/XIRR periods can therefore regress after a valid confirmation. **Suggested fix:** Before writing, preserve or merge compatible overlapping/adjacent coverage; reject confirmations that would shrink established coverage. If disjoint period evidence must coexist under schema 49, define a conservative non-destructive policy rather than overwriting silently. ## P2 - None. ## Verification - Focused Sprint 20C tests: **9 passed**. - `git diff --check`: clean. - Read-only review; **no files created or modified**.