# ADR 0003: Versioned statement imports - **Status:** Accepted - **Date:** 2026-09-11 - **Scope:** statement import pipeline, parse provenance, deduplication, apply/revert semantics ## Context Five bank statement contracts must be parsed (Alfa debit/credit XLSX, Ozon debit/credit PDF, Rocketbank debit PDF). Bank layouts drift over time. Money facts must never change until the user has reviewed the parse; losing a real operation is worse than holding a duplicate for review. The HTTP process must not parse files. ## Decision - An uploaded statement document is immutable: magic/MIME/size/hash checks, then storage encrypted per ADR 0004. Full extracted text is never persisted — only allowlisted statement fields plus page/row coordinates. - Every parse produces a versioned **parse run** (adapter + parser version) yielding immutable **source records** with document origin. A reparse creates a new run and diff; previous runs are never mutated. - State machine: `uploaded → queued → processing → review_required/ready → confirmed | failed`. Confirm applies the whole run atomically in a single DB transaction, creating only new operations/postings; existing duplicates only gain additional source links. Revert deletes exactly what that import created, with cascade reopen of affected periods. - Deduplication is conservative: auto-link only a stable bank identifier or a single unambiguous match; ambiguous candidates stay pending until the user decides. - Bank layout drift produces a **new parser version** with an explicit review diff — historical data is never silently reinterpreted. An adapter is beta-eligible only after the private fixture gate (see `docs/parsers/private-fixture-gate.md`). - Parsing runs in the Celery worker, isolated and resource-limited (memory, time, pages, rows, cells). ## Consequences - No row influences the ledger before explicit confirm; this invariant is enforced by tests (import confirm/revert idempotence). - Parse runs and source records grow storage; acceptable at beta scale, trimmed only with the source file deletion workflow. - Adapter code must explicitly detect its layout; a mismatch fails with stable diagnostics instead of guessing. ## Alternatives rejected - In-request synchronous parsing — blocks HTTP workers on untrusted files. - Mutable "latest parse" overwrites — destroys provenance and diffability. - Aggressive auto-dedup — risks dropping real operations.