Skip to content

v1.18.2: a transfer keyword alone no longer hides money sent or received; flag those for review - #59

Draft
gitluis wants to merge 2 commits into
add-mit-licensefrom
fix-one-sided-transfers
Draft

gitluis wants to merge 2 commits into
add-mit-licensefrom
fix-one-sided-transfers

Conversation

@gitluis

@gitluis gitluis commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #51 (add-mit-license, v1.18.1), the top of the redesign stack. You approved stacking in chat on 2026-11-04: "Stack on #51 (Recommended)". Detection was already changed by #46 and #49, and the Transfers screen by #19, so a branch from master would conflict with all three. Merge after #51. Draft until you've reviewed it.

Summary

Bug: a transfer keyword alone (Zelle, ACH, Venmo, PayPal, "transfer"…) made a transaction a transfer even when it had no other leg. A transfer is left out of income and spending, so money sent to or received from other people went missing from your totals.

Cause: detectTransfers() pass 2 (core.js) marked every unmatched keyword match as a transfer. A transfer only nets to zero when both legs are in your accounts. One leg alone is money that actually left (or arrived).

Found by: the new browser test (#55). The 30 sample transfers netted to −$230.00. All of it was six ZELLE TO M TORRES payments (May–Oct, $25–$60 each).

Fix (the option you chose: count as spending):

  • Only a pair is a transfer. A one-sided keyword match counts as spending (negative) or income (positive).
  • It keeps its own category when it has a real one ("PAYPAL *NETFLIX" stays Subscriptions). When it's only Other, or the Transfer label the built-in rules give every keyword, it gets the new category Money sent or Money received. Both are added to the default category list.
  • Keywords still matter for pairs: with one, two legs pair up to 2 days apart instead of 1, as before.
  • Your edits win: a type you set (including Transfer) or a category you set is kept, as since v1.16.23.
  • Re-detection undoes it cleanly: removing a keyword or re-importing puts a "Money sent/received" from detection back to the row's own category (isSentReceived() → ownCategories()) before detecting again.

Flagged for review (added in the second commit, your request)

Counting a one-sided match as spending or income is a guess, so each one also waits for your answer:

  • Transfers → To review lists them all (every date, newest first), each with Keep as spending (or income) and It's a transfer, plus Keep all when there's more than one.
  • The Transfers pill shows how many are left (screen readers hear "6 transactions to review"), and Transactions marks each row with a Review flag.
  • It's a transfer stores the Transfer type, as the drawer does, so the row leaves your totals. Keep stores reviewed: true in the edit, so it isn't asked again. A type you set in the drawer answers it too; changing only the category doesn't.
  • Worked out, never stored (needsTransferReview()), so it survives a boot from finlens-state.json, which doesn't re-run detection. Clear transaction edits removes the answers, so the rows are asked about again.

Version: v1.18.2 (both commits).

Effect on the samples

v1.18.1 v1.18.2
Transfers 30, net −$230.00 24, net $0.00
Spending $16,795.07 $17,025.07 (+$230.00, "Money sent")
Income $32,524.88 $32,524.88
Net (income − spending) $15,729.81 $15,499.81

Finance trade-offs to be aware of

  • A move to an account you haven't imported (a brokerage, savings at another bank, "ONLINE TRANSFER TO EXT ACCT") now counts as spending until you set its type to Transfer. That's the right default (the money did leave the accounts FinLens can see), but it overstates spending for transfers to your own untracked accounts. Set them once and the type sticks.
  • Money received counts as income. A friend paying you back for dinner isn't really income. If that's common for you, recategorize those rows, or tell me and a later PR could treat Money received as a spending offset instead.
  • Savings rate drops a little wherever these payments exist, because spending goes up. That's the more accurate number.

Related finding (not fixed here, one change per PR)

Keywords match anywhere in a word: ach matches "BEACH", "COACH" and "TEACHER". Before this PR, a one-sided "BEACH CAFE" charge was a transfer and hidden from spending. After it, the only effect left is that such a row can pair with a matching opposite amount 2 days apart instead of 1, which is unlikely. A whole-word match would be its own small PR.

AI Analysis — eval needs a re-run

The AI summary's spending, categories and monthly trend change with these payments. So docs/EVAL.md no longer matches the summary FinLens sends: the eval sandbox now sends "Spending: $17,025.07". Re-run the eval with your API key (showView('ai-eval')) and publish it per CLAUDE.md §21 before trusting its numbers. I couldn't run it, since it needs your key. The sample files are unchanged, so their hashes still verify.

How it was tested (headless Chromium 153, locally)

  • Smoke tests (v2.0.1: browser smoke tests in CI (Chromium + Edge) #55's smoke.cjs, run on this branch): 14 passed, 0 failed, only the known "-$0.00" bug. Every view renders with no errors, with and without data (25 views).
  • Product rules (v2.0.4: product-rule checks (bank names, version bump, duplicate versions) #57's check, run on this branch): 0 bank names, every version place says v1.18.2.
  • Scenario checks on the samples:
    • Import: all six Zelle rows are debit with Money sent, and the 24 remaining transfers net to $0.00.
    • Removing the zelle keyword: they go back to their own category (Transfer label, debit). Adding it back: Money sent again.
    • Setting one's type to Transfer by hand: it stays a transfer. Removing that edit: back to Money sent.
    • Setting one's category to "Gifts": it survives re-detection.
    • Transactions screen (All time): shows "Money sent".
  • AI Eval sandbox: builds its summary with no errors, and it matches a normal import.
  • Review flow (second commit):
    • Import: 6 to review, 6 rows in the panel, pill shows 6.
    • Keep one: 5 left, focus moves to the next row's Keep.
    • It's a transfer on one: 4 left, transfers 24 → 25, spending −$25.00.
    • Transactions list: shows the Review flag.
    • Keep all: 0 left, panel gone, pill back to plain "Transfers".
    • Removing and re-adding the zelle keyword: the answers stay (still 0).
    • Both themes: checked in light and dark screenshots.
    • After the second commit: smoke tests still 14 passed, 0 failed, and the product rules still show 0 bank names.
  • Not tested: real Edge/Chrome with your own data folder, an actual eval run, Safari/Firefox, screen readers.

How to reproduce the bug

Run this on the base branch, add-mit-license.

  1. Check out add-mit-license and open index.html in Edge or Chrome.
  2. Go to Settings → Data & reset → Start over (or use a fresh browser profile), then click Try it with sample data on the Welcome.
  3. Set the date range to All time.
  4. Go to Transactions → Transfers and read the count. Look for the six ZELLE TO M TORRES rows.
  5. Go to Transactions → Spending and read the total and the category list.

What goes wrong:

  • The Transfers count is 30, not 24, and the six ZELLE TO M TORRES payments ($25–$60 each, May–Oct) are listed as transfers, though they have no other leg in your accounts.
  • Spending reads $16,795.07, not $17,025.07. There is no Money sent category, so the $230.00 sent to another person is missing from your totals.

Manual testing (step by step)

  1. Check out this branch and open index.html in Edge or Chrome.
  2. Settings → Data & reset → Start over (or use a fresh browser profile), then Try it with sample data on the Welcome.
  3. Transactions → Transfers: the count should be 24, and the six ZELLE TO M TORRES rows shouldn't be there. Read the note under Transfer keywords: it explains the new rule.
  4. Transactions → Spending (date range All time): the total should be $17,025.07, with a Money sent category of $230.00.
  5. Open one Zelle row, set its Type to Transfer, and save. It should move to Transfers, and Spending should drop by that amount. Set it back to Expense.
  6. Go back to Transfers. To review should list the remaining Zelle rows, and the Transfers pill should show the count. Click Keep as spending on one: the count drops by one, and focus moves to the next row. Click It's a transfer on another: it leaves the list and shows up among the transfers. Transactions should show a Review flag on the rows still waiting.
  7. On Transfers → Transfer keywords, remove zelle. The Zelle rows should show the category Transfer, still counted in Spending. Add zelle back: they show Money sent again.
  8. With your own data (your real folder): work through Transfers → To review: It's a transfer for moves between your own accounts, Keep for money that really left or arrived.
  9. The footer and Settings → General should say v1.18.2.

Please check before merging

  • v1.18.1: add MIT license #51 and the stack below it are merged first
  • You're happy counting one-sided matches as spending/income (step 7 on your own data)
  • Eval re-run with your key and published, or a follow-up PR planned
  • Version v1.18.2 isn't taken by anything merged before this (renumber if so)

https://claude.ai/code/session_013Z8iBUNj6C627mxmm1CurV


Generated by Claude Code

Only a pair of legs is a transfer. A one-sided keyword match (a Zelle payment
to a person) counts as spending or income, as "Money sent" / "Money received"
when it has no better category. In samples/ this restores $230.00 of spending
and the remaining transfers net to $0.00.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Z8iBUNj6C627mxmm1CurV
@gitluis gitluis added the bug Something isn't working label Nov 4, 2026 — with Claude
They still count as spending or income, but each waits for an answer:
Transfers -> To review lists them (Keep as spending/income, It's a transfer,
Keep all), the Transfers pill shows the count and Transactions flags each row.
The flag is worked out, never stored; answers are edits (type, or reviewed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Z8iBUNj6C627mxmm1CurV
@gitluis gitluis changed the title v1.18.2: a transfer keyword alone no longer hides money sent or received v1.18.2: a transfer keyword alone no longer hides money sent or received; flag those for review Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants