# Sprint 17A pre-merge evidence

- Candidate lineage: `2c53019`, `ccac1c7`, `1984433`, `033e1b7`, `71183ca`, `8f6eee0`, `e0d03f3`, `b5c3ddd`, `e5b3484`; current frozen candidate `fbe08a94555e003f0fd082be1f68eaaec330f83c`
- Base / current production commit: `039d005696e4ef097427d15b74a26a3c09e86a30`
- Schema migration: none
- Production writes: none
- Production database integrity: `ok`; FK findings: `0`; read-only probe SHA unchanged
- Backend full suite: `830 passed`
- Frontend suite: `198 passed`
- Frontend typecheck and production build: passed
- Compileall, Git-Safety, diff check, targeted Ruff: passed

## Review discrepancy

- `228`: actionable candidates (`needs_review`; no current `pending`/`transfer_candidate`). This is the canonical `total_open`.
- `265`: legacy overview count included 37 `covered_by_source` Migros receipt-detail candidates in addition to the 228 actionable candidates. Those 37 have no independent monetary effect and no decision to make.
- `0 Vorschläge`: the old review page fetched and reclassified up to 100 candidates synchronously; production row classification was N+1/slow, and request failure left the client model at its zero-valued default. The UI then rendered the successful `Alles geprüft` empty state despite an API failure.

## Frozen production aggregate proof with final candidate code

- `total_open=228`, `proposal_ready=47`, `decision_needed=161`, `special_cases=20`
- Overview `review_count=228`; canonical page `filtered_total=228`
- Review default page: 25 rows, `has_more=true`
- Confirmed transactions: 1,717 total; July 2026: 48 filtered/page rows; transaction page including content digest about 0.035 s
- Canonical review page including the complete classifier/rendering digest about 2.34 s; server-side decision union `181 = 161 + 20`
- Reconciliation differences for all-time, July, and 2026-01-01..2026-07-31: income, expense, net all CHF 0.00
- Neutral transfer rows: 69; income/expense/budget/net effect CHF 0.00
- Transfer volume: 69 rows resolve to 53 logical units; the read-only production summary matches an independent logical-unit aggregation and leaves the database hash unchanged.
- Transfer membership joins are one-row-per-transaction; conflicting memberships fail closed with null volume and explicit quality metadata instead of multiplying financial rows. Production has zero such conflicts.
- A malformed non-transfer row in multiple transfer relations also nulls all scoped transfer volume; Household Overview distinguishes integrity conflicts from missing-FX cases.
- Cockpit, Budget-vs-Ist, monthly/chart and category-summary contracts propagate missing-FX, unlinked-refund and transfer-integrity status; changed frontend analytics surfaces render a visible partial-data warning.
- Budget status/planning and category detail use canonical CHF effects, never foreign original amounts as CHF forecast values; partial status is visible and unconverted detail amounts retain their original currency label.
- The legacy category-summary endpoint remains a top-level list; the explicit `summary-v2` endpoint carries the quality envelope.
- Delayed transaction responses are invalidated on component unmount and cannot rewrite a destination URL.
- Migros links: 35 links to 35 distinct money targets
- Confirmed-import duplicate suppression: 1 row
- Confirmed import batches: 1

## Independent review remediation

- Initial exact-commit review: `CHANGES_REQUIRED`, P1 linked-refund persistence/read-model mismatch.
- Closure: future imports persist `reversal_of_transaction_id` in the typed column; legacy JSON-note representation remains readable behind `json_valid`; effective origin category is used in financial aggregation and transaction category filtering/display.
- Focused future/legacy reversal regressions: passed.
- Post-fix backend suite: `824 passed`; frontend suite: `189 passed`; static gates, typecheck and build passed.
- Ten private account/date filter combinations reconciled with maximum CHF 0 difference.
- Production contains 24 confirmed refunds, currently 0 linked and 24 unlinked; the production read-only reconciliation remains exact to CHF 0.00 and the DB digest remained unchanged.
- Missing/unconfirmed non-CHF conversion is excluded from CHF totals and marked partial; original amount/currency remains visible. Production currently has 0 such confirmed rows.
- Proposal totals, filters and items now share active, amount-compatible category eligibility; wrong-type and inactive-category regressions passed.
- Transaction cursors bind the current data version; mutation-between-pages is rejected with 409 and the client restarts from page one.
- Review source facets are global, and generation guards discard obsolete filter/page responses while clearing stale failed-filter rows.
- Year charts/status reuse one canonical effects scan; focused scan-count regression passed and the read-only production year chart completed in about 0.013 s.
- A subsequent exact-`1984433` review found weak COUNT/MAX cursor versioning plus missing partial-state propagation. `033e1b7` uses a deterministic content digest over every rendered/paging dependency, marks unlinked refunds partial, and propagates missing-FX/refund status through the monthly summary.
- Exact `033e1b7`: backend `826 passed`; frontend `191 passed`; typecheck/build/static gates passed. Read-only productive transaction page including the content digest completed in about 0.034 s for 1,717 rows; DB digest unchanged.
- The exact-`033e1b7` review closed those earlier findings but found three additional gates: arbitrary/excess refund links, missing-FX transfer-volume/chart status, and weak review-cursor versioning.
- Exact `71183ca` closes them with eligible and cumulatively bounded refund effects plus locked writer revalidation, null/partial missing-FX transfer volume propagated through monthly/chart contracts, and content-digested review cursors with post-read consistency checks.
- Lifecycle authority is now exactly `household_batch_id IS NOT NULL AND status='needs_review'`; stale `requires_review` flags neither close nor reopen records. Shared helper, Overview, cockpit KPI and canonical review all return production `228`; subgroups remain `47/161/20`.
- Exact `71183ca`: backend `828 passed`; frontend `191 passed`; typecheck, production build, compileall, F/I Ruff, Git-Safety and diff check passed. Production probes used immutable read-only SQLite and left its SHA unchanged.
- The exact-`71183ca` review then found that review cursors omitted mutable classifier lookup tables, the default decision tab still filtered page-locally and did not recover a stale cursor, and the UI did not enforce `can_confirm` or category-type constraints.
- Exact `8f6eee0` digests all classifier/history/merchant/rule/recurring/mapping/category dependencies; sends the server-side `needs_decision` scope; restarts stale review pagination from page one; and enforces server-provided confirmability plus expense/income category types. Dedicated special cases remain blocked by the server batch contract.
- Exact `8f6eee0`: backend `828 passed`; frontend `193 passed`; typecheck, production build, compileall, F/I Ruff, Git-Safety and diff check passed. Immutable production probes returned `228`, `47/161/20`, and decision-union `181`; database SHA unchanged.
- The exact-`8f6eee0` review found two remaining display defects: the overview still read the CHF-zero budget-effect field instead of separate transfer volume, and signed imported expenses could render with a double minus.
- Exact `e0d03f3` displays nullable `neutral_transfer_volume_chf` with an explicit missing-FX state and normalizes stored signs before adding the semantic income/refund/expense sign. Backend remains at `828 passed`; frontend is `195 passed`; typecheck, production build, Git-Safety and diff check passed.
