## CHANGES_REQUIRED - **P1 — Imported linked refunds are treated as unlinked and do not offset expenses.** `src/jarvis_finance/services/household_import.py:1795-1824` stores `reversal_of_transaction_id` only inside `notes`, while `src/jarvis_finance/services/household_financials.py:57-69` joins exclusively on the actual `t.reversal_of_transaction_id` column. Focused importer probe produced an expense plus refund with both column links `NULL`; canonical summary incorrectly returned `expense_chf=8.25`, `net_chf=-8.25`, and `unlinked_refund_count=1` instead of zero expense/net. The new test at `tests/unit/test_household_import_v1_golden.py:856-869` bypasses the production writer by inserting the column directly, masking the defect. **Remediation:** persist the matched origin into `budget_transactions.reversal_of_transaction_id` during import confirmation, backfill/read the legacy notes representation if existing rows require compatibility, and add an import-path reconciliation assertion to `test_unique_reversal_deterministically_offsets_original_transaction`. - **P1 — Review counts, state filters, and rendered item state use different proposal-ready predicates.** `src/jarvis_finance/services/household_import.py:2476-2504` classifies every non-special row with a non-null `proposed_category_id` as `proposal_ready`. But `_human_review_items` at `:2294-2317` revalidates that the category is active and has the expected income/expense type; an inactive, deleted, or wrong-type category is rendered `decision_needed`. Consequently global counts and `state=proposal_ready`/`decision_needed` can disagree with returned items. **Remediation:** define one canonical SQL/service predicate joining active `budget_categories` with the amount-derived category type, and reuse it for totals, filters, and item-state construction. Add stale/inactive and wrong-type category regressions. - **P1 — Transaction pagination is not snapshot-consistent despite exposing `data_version`.** `src/jarvis_finance/services/household_import.py:2194-2195` scopes cursors only to filters; `data_version` is calculated later at `:2246-2259` and is neither embedded nor checked. A write between pages changes `total_all`/`filtered_total` while the frontend appends the old page to the new snapshot at `frontend/src/pages/HouseholdTransactionsPage.vue:88-94`, allowing displayed counts and loaded rows to describe different datasets. Review pagination correctly binds its version, so the contracts are inconsistent. **Remediation:** calculate the transaction version before decoding the cursor, include it in cursor scope, reject stale cursors with `409`, and have the frontend restart pagination after that response. Add a mutation-between-pages test. - **P2 — Review source filtering is limited to sources appearing on the currently loaded page.** `frontend/src/pages/HouseholdReviewPage.vue:158` derives filter choices from `review.items`; with a 25-row page, sources present only on later pages cannot be selected even though the backend supports them. **Remediation:** return canonical source facets/options independently of page rows, or use the complete import-profile option list. ### Verification - Confirmed exact clean HEAD `2c5301913c01a424d03443b1c2f64e7ac5669185`; `git diff --check` passed. - Focused backend tests: **7 passed, 23 deselected**. - No production data queried, no migrations run, and no files created or modified.