whisper-money/app
Víctor Falcón e065d4ab65
feat(ai): defer per-transaction categorization until onboarding completes (#536)
## What

During onboarding, the per-transaction AI categorization listener no
longer runs. The AI automation rules generated in the suggestions step
cover the bulk of the imported transactions (~70%). On completion, a
single batch pass categorizes whatever is still uncategorized.

## Why

Running per-transaction categorization on every transaction created
during onboarding is wasteful: most are about to be covered by the AI
rules generated at the end of the flow. Defer the AI spend to one batch
over the remainder.

## How

- **`CategorizeTransactionWithAi` listener** — skips when `!
$user->isOnboarded()`. Covers every txn-creation path, since it hangs
off the model `created` event.
- **`CategorizeOnboardingTransactionsJob`** (new) — dispatched from
`OnboardingController::complete` after `onboarded_at` is set. Snapshots
`whereNull('category_id')->whereNull('description_iv')`, so transactions
already categorized by the AI rules (or manually in the categorize step)
are excluded. Chunks by `group_batch_size` and runs `AiCategorizer` per
chunk. Runs on the dedicated `ai` queue.
- Gated by `AiCategorizationGate::allows()` (rollout flag) — automatic
system behavior, gated like the real-time tier, not the explicit
backfill.

## Flow

1. `syncing` → transactions created, **no** per-transaction AI
2. `ai-suggestions` → AI rules cover the bulk
3. `categorize-transactions` → user manually categorizes a few
4. `complete` → batch-categorize the rest, skipping anything already
labeled

## Tests

- Listener skips transactions created while onboarding; still
categorizes post-onboarding.
- Job categorizes the remainder, leaves rule/manual categories
untouched, no-ops for ineligible users.
- `complete` marks the user onboarded and queues the batch job.

Full Ai + Onboarding suites green (112/112). Pint clean.
2026-06-15 17:33:26 +02:00
..
Actions feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
Ai/Agents feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Console/Commands feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Contracts feat(open-banking): finalize bank connection without a session via state token (#498) 2026-06-06 12:12:07 +02:00
Enums feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Events Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
Exceptions/Banking Handle transient EnableBanking sync failures (#358) 2026-05-06 09:24:05 +02:00
Features feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Http feat(ai): defer per-transaction categorization until onboarding completes (#536) 2026-06-15 17:33:26 +02:00
Jobs feat(ai): defer per-transaction categorization until onboarding completes (#536) 2026-06-15 17:33:26 +02:00
Listeners feat(ai): defer per-transaction categorization until onboarding completes (#536) 2026-06-15 17:33:26 +02:00
Mail feat(leads): add user lead re-invite campaign (#432) 2026-05-26 08:35:31 +02:00
Models feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Notifications fix: verify email via signed link without requiring login (#490) 2026-06-05 10:01:32 +02:00
Policies refactor(policies): extract HandlesUserOwnership trait (#478) 2026-06-03 17:43:30 +02:00
Providers feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
Rules feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
Services feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00