* feat(inbox): match non-invoice documents via prominent amounts
Bankintyg, bank agreements and other documentKind "other" PDFs carry no
invoice-style total, so extraction correctly left totals.total null and the
document became structurally unmatchable: findUnderlagCandidates hard-drops
items without a comparable amount and the picker lost the 40% amount signal.
- extraction: new prominentAmounts[] field (amount + document's own label),
populated only when totals.total is null; account/org/phone/reference
numbers and zero amounts excluded. totals.total semantics untouched.
- matching: bestProminentAmountVariance() tries each printed amount and
feeds calculateMatchConfidence at reduced weight (0.3 vs 0.4) in both the
agent candidate scorer and TransactionMatchPicker.
- UI: inbox rail shows the detected amounts read-only for such documents,
list falls back to a single distinct prominent amount, and extraction no
longer reads as "found nothing".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm9QgdyNAFaWiz6Ww7pgb
* fix(inbox): discount prominent-amount fallback instead of reweighting it
Skeptic pass refutations on the first commit: normalized weighting made a
reduced amount weight self-defeating. Date + exact fallback amount with no
merchant scored (0.25+0.3)/0.55 = 1.0 ("100% sakerhet" on a wrong same-day
transaction), and a DISAGREEING fallback amount scored above a disagreeing
invoice total (0.67 vs 0.60) because shrinking the weight also shrank the
penalty.
- score fallbacks at full amount weight, then multiply by a flat
FALLBACK_CONFIDENCE_FACTOR (0.85): agreement caps below certainty,
disagreement stays at least as damning as for a real total.
- agent candidate surface additionally requires the document date within
DATE_TOLERANCE_DAYS, so an avtal listing 349 kr no longer matches every
future 349 kr charge from the same counterparty.
- bestProminentAmountVariance returns which amount matched + its document
label, and the match reason names it ("Exakt belopp i dokumentet: 2 500
SEK (Engangspris)"): no more bare "Exakt belopp" reaching the agent while
total_amount is null.
- prompt: prominentAmounts restricted to non-invoice documentKinds, and
never a parking spot for an unreadable invoice total.
- fix the stale "deliberately the same list" comment on
EXTRACTED_FIELD_ACCESSORS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm9QgdyNAFaWiz6Ww7pgb
* fix(receipt-hunt): never propose on the prominent-amounts fallback
Second skeptic pass: the nightly hunt is a third consumer of
scoreUnderlagCandidates and inherited the fallback unaware. A bankintyg
whose printed "Insatt belopp" equals a same-day outflow scores 0.85, which
clears CERTAIN_CONFIDENCE (0.8) and skips LLM adjudication, on a pairing
wrong by construction (the hunt scans outflows only; "Insatt belopp"
labels an inflow), with document_amount null in the approval preview.
UnderlagCandidate now carries amountSource ('total' | 'prominent') and
selectProposals drops fallback-scored candidates. Non-invoice documents
stay reachable through the manual picker and the agent candidate surface,
both of which have a human reading the amounts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm9QgdyNAFaWiz6Ww7pgb
* fix(inbox): round fallback confidence via roundOre, not the naive pattern
The two confidence discounts (and their test) tripped the naive-ore-round
antipattern ratchet (625 vs baseline 622); use the sanctioned helper.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm9QgdyNAFaWiz6Ww7pgb
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>