## Verdict: **BLOCKERS FOUND — do not release** ### P1 — SQLite v51 migration fails on valid v50 data and leaves a partially migrated database - **References:** - `src/jarvis_finance/storage/migrations.py:2699-2734` — `_create_current_source_coverage_and_truewealth_activity_v1()`, especially `ux_cash_snapshot_semantic_identity` - `src/jarvis_finance/storage/migrations.py:2874-2889` — `apply_migrations()` - Historical writer: `src/jarvis_finance/services/cash_service.py:503-538` — `confirm_cash_snapshot()` v50 permitted multiple identical `cash_account_snapshots`; the new migration directly creates a unique index over those existing rows without first detecting or safely reconciling duplicates. A synthetic, internally consistent, FK-clean pre-v51 database containing two snapshots that were legal under v50 produced: ```text IntegrityError UNIQUE constraint failed: cash_account_snapshots.account_id, cash_account_snapshots.snapshot_type, cash_account_snapshots.balance_date, cash_account_snapshots.amount_chf, cash_account_snapshots.source ``` Worse, `apply_migrations()` is not atomic around the compatibility migration. After failure, newly added v51 columns were already present while migration version 51 was not recorded. This creates a persistent partial-migration state. **Required release fix:** preflight/deterministically reconcile legacy duplicates or use a compatible uniqueness strategy, and make v51 application transactional or otherwise restart-safe. Add a migration test starting from a legal duplicate-bearing v50 database. --- ### P1 — Backend/frontend contract mismatch makes both new household confirmation paths unreachable in the product UI - **Backend additions:** - `src/jarvis_finance/services/household_import.py:1295-1422` — `confirmed_row_tokens` - `src/jarvis_finance/services/household_import.py:2009-2011` — `confirm_review_candidates` - **Missing frontend contract:** - `frontend/src/api/household.ts:380-385` — `HouseholdImportRequest` defines neither field - `frontend/src/api/household.ts:582-583` — preview/confirm calls use that incomplete request type - **UI remains incompatible:** - `frontend/src/pages/HouseholdImportsPage.vue:89-94` — category choice has no explicit row-confirm control - `frontend/src/pages/HouseholdImportsPage.vue:119-120` — UI says confirmation remains blocked and disables Confirm whenever `business_ready_for_confirm` is false - `frontend/src/pages/HouseholdImportsPage.vue:366-378` — `currentRequest()` serializes no `confirmed_row_tokens` - `frontend/src/pages/HouseholdImportsPage.vue:419-425` — `confirmImport()` rejects non-business-ready previews and never sends `confirm_review_candidates` Consequences: 1. Selecting a category for an unresolved row cannot promote it, because the required `confirmed_row_tokens` never reaches preview/confirm. 2. The new safe “register unresolved rows as review candidates without financial effect” path cannot be invoked because the UI blocks confirmation and never sends `confirm_review_candidates: true`. The backend unit tests exercise hand-built payloads, so they do not detect this production contract break. --- ### P2 — TrueWealth row identity rejects valid repeated same-day activities - **References:** - `src/jarvis_finance/imports/truewealth_tax_statement.py:153-176` — `_parse_activities()` `source_row_fingerprint` contains date, event type, ISIN, quantity, gross, and tax, but no source occurrence/reference. Two legitimate same-day purchases of the same instrument and quantity therefore collide, and the parser rejects the entire statement as “not uniquely identifiable.” A narrow synthetic counterprobe with two identical same-day buys reproduced the `ValueError`. This fails closed rather than double-counting, but valid official statements can become unimportable. Add a stable source-row occurrence discriminator without weakening replay dedupe. ## Verification - Targeted backend suite: **87 passed** - New wealth frontend test: **1 passed** - `git diff --check`: **passed** - Privacy review: no newly exposed raw account identifiers or raw source rows found in the changed response surfaces. - Financial double-counting review: no additional confirmed double-count path found in the new PostFinance component valuation write; the existing two-component storage remains additive without storing the portfolio total as a third performance component. - **Files created or modified:** none. Review and probes were read-only/in-memory. - **Issues encountered:** repository-local `.venv` was absent; tests succeeded with the available Python environment.