## Findings - **P1 — Cluster decisions can assign income categories to expenses (and vice versa), then materialize them as business-ready.** `src/jarvis_finance/services/household_import.py:891-895,459-490` `valid_categories` contains every active category without its `category_type`; cluster validation checks only category existence. Reproduced two negative VISA rows bulk-assigned to `cat_other_income`: preview reported `business_ready_for_confirm=True`, and confirm persisted both as `transaction_type='expense'` with an income category. **Fix:** retain `category_type` in category metadata and require `expense` categories for expense clusters and `income` categories for income clusters. Apply the same semantic validation to row-level overrides and canonical family resolution before setting `requires_review=False`. - **P1 — Migros matching can persist a `linked` receipt with no money movement.** `src/jarvis_finance/services/household_import.py:940-969,974-992,1530-1543` The current-import money pool includes pending rows and other non-duplicate rows regardless of disposition or transaction semantics. Reproduced an exact pending VISA/Migros row plus receipt: preview declared the receipt linked and business-ready; confirm persisted `household_migros_links(status='linked', money_candidate_id=NULL, money_transaction_id=NULL)`. **Fix:** restrict money pools to durable, writable, non-transfer expense movements; exclude pending/superseded/card-payment/transfer rows. During confirm, fail atomically if a `linked` record cannot resolve to exactly one candidate or transaction. Add a DB check/trigger enforcing exactly one money reference for `status='linked'`. - **P2 — User-confirmed unmatched transfers are underreported by 50% in household overview.** `src/jarvis_finance/services/household_import.py:1340-1369,1611-1637` An unmatched user-confirmed transfer materializes one transfer leg, but overview divides all transfer volume by two under the assumption that every transfer has two balanced legs. Reproduced a CHF 91 unmatched transfer displaying `neutral_transfers_chf='45.50'`. **Fix:** distinguish paired transfer legs from single-leg user-confirmed unmatched transfers; halve only paired-leg totals and add unmatched transfer absolute amounts once. ## Verification - Focused backend tests: **72 passed** - Full `make PYTHON=.venv/bin/python verify`: **793 Python + 177 frontend tests passed** - `git diff --check`: passed - Files modified: **none** - Issues encountered: none beyond the findings above.