Review finding on #330, two defects with one root cause.
Desjardins' fingerprint is date/description/montant/solde — four labels any
Canadian bank could emit — and MIN_SIGNATURE_LABELS = 4 did not deliver the
property it promised, because matching was by SUBSET. Any
Date;Description;Montant;Solde file was announced 'Format Desjardins reconnu'.
A variant built only from generic labels must now describe the header exactly;
one carrying a discriminating label (chequenumber, categorie, memo) keeps
subset matching, so extra columns stay fine once something identifies the bank.
Worse, a matched signature's single amount column short-circuited the
sparse-complementary scan instead of being arbitrated against it. A
Date;Description;Debit;Credit;Montant;Solde file reads correctly as debit/credit
before #330 and became one unsigned column after, importing every deposit as an
expense. The scan now runs first; a signature's amount column only wins when the
pair contains it — which is what RBC's genuine Cheque Number / CAD$ case needs,
and it still passes.
Refs #330
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review finding on #327. detectDescriptionColumn returned the lexically
preferred column with no check on the data, unlike the date (replayed at 0.8)
and the amount (constrained to the shape candidates). The dictionary lists
'transaction' as a description keyword, and Tangerine exports
Date,Transaction,Name,Memo,Amount where Transaction holds DEBIT/CREDIT — so the
description moved off the merchant name and keyword categorisation died.
Cardinality tells free text from an enum: a description repeats almost nothing,
an enum repeats almost everything. Average length does not — Note and Libelle
are both short, so a length veto would reject legitimate columns.
This fixes the cause. #330 had rescued the case through the Tangerine signature
alone, leaving every unrecognised file with a Transaction column broken; that
test now asserts the correct mapping with and without a signature.
Refs #327
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review finding on #325. The debit/credit rule tested isNaN(debit) && isNaN(credit),
which only caught the case where BOTH sides fell. The unused column carries 0,00
in exactly the files this rule exists to fix, so an unreadable debit beside a
0,00 credit computed 0 - 0 = 0 and imported silently — the very bug, one cell
over. Replayed on the PR's own unused-column-zero fixture with a currency
suffix: 6 transactions imported at 0,00 with no error row.
A mapped cell that is not empty but does not parse now fails the row whatever
its sibling holds. An EMPTY cell keeps meaning 'this column does not apply to
this row' and contributes zero, which is the normal shape of the format.
Refs #325
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Last link of the ten-link import-format stack. Links 1-9 deliberately wrote
no changelog and no documentation, to avoid a conflict at every level of a
linear pile; this link owes all of it.
- ADR 0019 records the structuring decision: the import format is a fully
persisted value, never re-inferred. It documents the three independent paths
by which it used to be lost (hardcoded restore, header drift, data export),
and why a single codec with a completeness test closes the class rather than
a composed type -- the two carriers are structurally incompatible
(`has_header` is boolean on one and number on the other). `template_id` is
recorded as a provenance label, never re-read as format.
- `docs/architecture.md` gains a dedicated "Import CSV" section covering the
codec, the lexical detection layer and its separate dictionary module, the
bank signatures, the now-mandatory preview step, and sources/templates in
the SREF envelope. Migration v17 and its four CHECK-guarded columns are
listed in the migrations table.
- Stale counts corrected against the tree, not by arithmetic: 16 -> 17
migrations (both files), `src/components/import/` 13 -> 14, `src/utils/`
4 -> 13. Tables (20) and indexes (24) were re-measured and are NOT stale --
v17 is an ALTER TABLE only -- so they are left as they are, with the reason
written down. ADR 0018, missing from the ADR table, is added.
- The user guide and the `docs.*` keys in both locales carry the same new
import journey: automatic detection, confidence score, mandatory preview
with its signed recap, sign inversion, recognised bank formats, drift panel,
and the safe repair path.
- Both changelogs carry the same entries, translated, verified section by
section including issue references. The `public/` copies sync automatically
via `syncChangelogs()`.
1176 vitest green, build clean, cargo check clean. No DB migration.
Resolves#332
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>