Skip to content

The category picker and reimbursable controls show the latest after a stale refusal, instead of saying "Refresh and try again" (#139) - #251

Merged
smilne3 merged 4 commits into
mainfrom
fix/139-picker-reloads
Nov 5, 2026
Merged

smilne3 merged 4 commits into
mainfrom
fix/139-picker-reloads

Conversation

@smilne3

@smilne3 smilne3 commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

This finishes #139. When a change to a transaction was refused because the transaction had gone stale, the picker and the reimbursable controls said "Refresh and try again". The installed app has no refresh button. Now they show the latest themselves.

What changes for you

This only changes what you see when a change on Transactions is refused because the page is out of date:

What happened since the page loaded Before Now
The bank removed the transaction, as it does when a pending one posts "Your bank removed that transaction. Refresh and try again." the list reloads: the removed row goes, and the posted one shows, ready to pick again
The transaction is gone "That transaction no longer exists. Refresh and try again." the list reloads without it
The category was deleted, on the other phone say "That category no longer exists. Refresh and try again." "That category no longer exists. Showing the latest.", and the category list updates
The bank changed the transaction during the save "This transaction just changed. Refresh and try again." "This transaction just changed. Showing the latest.", and the list reloads

The reimbursable tick and editor, which already reloaded, say "Showing the latest." too. For a removed transaction they now reload as well, so one sheet no longer gives two answers.

Other refusals don't change. A credit-card payment, for example, still says why, and doesn't reload. Two people picking for the same transaction isn't one of these: the later pick simply wins, as before.

What's still missing: when the row itself is gone, its message goes with it, so nothing says the change wasn't saved. That was already true of the reimbursable controls. It's filed as #252, which suggests a notice above the list, like the one Goals has.

How

  • components/CategoryPicker.tsx: after a refusal with status 404 or 409, it calls router.refresh(), as CategoryRulesCard and the reimbursable controls already do. Any other refusal still doesn't reload.
  • app/api/transactions/categorize/route.ts:
    • The four stale-row refusals say "Showing the latest." instead of "Refresh and try again."
    • A removed transaction and a deleted category now answer 409 rather than 400, so the picker reloads for them.
    • Only the picker calls this route.
  • app/api/reimbursable/route.ts: the same words for its three stale-row refusals. A removed transaction answers 409 rather than 400.

Checked

  • New tests:
    • A 404 and a 409 each reload once and show the route's words.
    • A deleted category's message stays while the reload leaves the row as it was, and the options lose the category.
    • A 400, a 500 and a 503 don't reload.
    • Both routes answer each stale refusal with its status and words.
  • Mutations: I broke each guard in turn (12 mutants), and tests caught all 12. They included:
    • no reload
    • a reload after only one of the two statuses, or after every refusal
    • a change from the server that keeps the message
    • each of the two routes' words and statuses put back
  • Locally: vitest passes 2496/2496 in both time zones. tsc, lint, check:secrets, check:invariants and next build all pass.

Review

Code and behaviour: nothing Critical.

  • It confirmed:
    • this route can't answer the environment guard's 409
    • nothing else reads these statuses
    • a name that never was a category can't come from the picker
    • two quick picks are safe
  • Three Important findings:
  • Not done: the category-rules design spec still quotes the old statuses and words. It's a dated record that later fixes haven't kept current either.

No phone check

None of these can be brought about on purpose without changing your real data. Deleting a category takes its rules with it, and the bank decides when a pending transaction posts. A preview won't help either: a pick there still saves to your real data, and only rule learning is off. The screens themselves don't change, so I'd merge this without a phone check.

Closes #139.

🤖 Generated with Claude Code

smilne3 and others added 3 commits November 5, 2026 11:38
…saying "Refresh and try again" (#139)

The categorize route answered four refusals with "... Refresh and try
again.": the transaction gone (404), removed by the bank (400), the
category deleted (400), or the row changed under the write (409). The
picker showed the words as they stood, and the installed app has no
reload button, so the person was told to do something they couldn't,
while the row kept its stale value.

The picker now reloads itself after a 404 or 409, as CategoryRulesCard
and the reimbursable controls already do, and the route says "Showing
the latest." instead. The removed row and the deleted category answer
409 rather than 400, since they're stale rows too, so the reload removes
the row or updates the list. The refusal's words stay through the reload
that moves the select, so it doesn't change with nothing to say why.
Any other refusal still neither reloads nor survives the next change
from the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e at the next change (#139)

The test's second pick was another refusal, which clears the stale mark itself, so it could not see a new pick that forgot to. A request lost in flight can.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ther device's pick (#139)

The route has no check on a pick another device made: the later pick wins. Its 409 is the bank removing or re-tagging the row, or a deleted category.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
every-dollar-counts Ready Ready Preview Nov 5, 2026 5:21pm UTC

…s say "Showing the latest" too (#139)

From the review of #251:

- The flag that kept a stale refusal's words through the reload is gone.
  The route's 409s almost never leave the row on screen and change it,
  and when the reload left the row alone, the flag kept the words past
  the next unrelated change. The picker still reloads after a 404 or
  409, and its words now go at the next change, as any refusal's do.
- A row that's gone takes its message with it when the list reloads.
  That's also true of the reimbursable controls, so it's filed as #252
  rather than half-fixed here.
- The reimbursable route's stale-row refusals say "Showing the latest."
  too, since those controls already reload on a 404 or 409. Its removed
  row answers 409 rather than 400, so the controls reload for it as
  well, and one sheet no longer gives two answers for one removed row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@smilne3 smilne3 changed the title The category picker reloads itself after a stale refusal, instead of saying "Refresh and try again" (#139) The category picker and reimbursable controls show the latest after a stale refusal, instead of saying "Refresh and try again" (#139) Nov 5, 2026
@smilne3
smilne3 merged commit ccd826c into main Nov 5, 2026
6 checks passed
@smilne3
smilne3 deleted the fix/139-picker-reloads branch November 5, 2026 17:33

This branch was successfully deployed

1 active deployment
Preview — 815b70fb Deployed Nov 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Refresh and try again" has nothing to tap in the installed phone app

1 participant