## Outcome: **Not closed — 2× P2** No P0 or P1 findings. ### P2 — Cockpit repeats unbounded full-history scans - **Location:** `src/jarvis_finance/services/household_cockpit.py:342-348`, invoked at `:463` and again at `:619`. - `get_household_cockpit_data_version()` runs aggregate queries over every row of both `budget_transactions` and `budget_transaction_candidates`. The `SUM(length(...rowid...))` requires a table scan. - The version function is called twice per cockpit request, despite the request already running inside a SQLite read transaction (`:439-447`). - **Proof:** SQLite `EXPLAIN QUERY PLAN` reported `SCAN x` for the equivalent aggregate. A trace of one synthetic cockpit request showed both full-history aggregate queries at statements 2–4 and again at 25–28, plus every small dependency table being read twice. - This does not satisfy the explicit closure requirement of no repeated unbounded full-history scans. Use the read transaction for consistency and calculate a bounded/revision-backed version once, or otherwise avoid rescanning full history. ### P2 — Generated category drilldowns fail for historical inactive categories - **Locations:** - Cockpit emits the transaction’s stable category ID without considering active status: `src/jarvis_finance/services/household_cockpit.py:165-200`. - Transactions endpoint rejects any non-active category ID: `src/jarvis_finance/services/household_import.py:2311-2318`. - **Counterexample proof:** Created a confirmed CHF 10 expense under `cat_food`, then archived that category. Cockpit returned: - category `cat_food` - expense `10.00` - a `category_id=cat_food&financial_effect=expense` drilldown - Following the corresponding transaction query raised **422 `unknown or inactive category_id`**. - Historical category effects therefore cannot always drill down and reconcile. Validate that the category exists rather than requiring it to remain active, or define a separate historical-category contract. ## Verified closures - Exact active account IDs are validated fail-closed with 422; duplicate, empty, inactive, and unknown IDs are rejected. - Repeated `account_id` parameters propagate through cockpit and transaction drilldowns. - Valid linked refunds, cumulative over-refund exclusion, and uncategorized drilldowns reconcile. - Account-scoped budget and forecast values are explicitly unavailable. - Snapshot reads are SQLite transaction-consistent; covered dependencies include transactions, candidates, plans/baselines, transfers, recurring inputs, Migros links, and settlements. - Review counts are date/account scoped; unbound candidates force partial/unavailable scope. - Neutral transfer, refund, and reconciliation semantics passed targeted probes. ## Verification - `137 passed` across household-focused backend tests. - Dedicated cockpit suite: `14 passed`. - Python compile and `git diff --check`: passed. - No tracked/source files modified. Test/compile execution may have refreshed ignored `__pycache__` artifacts.