ApplySingleAutomationRuleJob was only ever asserted to be *pushed* under
Queue::fake() — its handle() body (chunked apply, progress accumulation)
and failed() branch had no coverage. ReEvaluateTransactionRulesJob's
failed() branch was likewise untested.
Add direct handle() and failed() tests for the apply job, and a failed()
test for the re-evaluate job, pinning the progress-cache payloads so the
'failed' status and preserved counts can't silently drift.
The apply flow resolves matching transaction IDs into a snapshot cached
for up to 15 minutes and keyed only by rule.updated_at. Both the sync
path and ApplySingleAutomationRuleJob then applied the rule's category to
every id in that snapshot without re-checking eligibility.
A transaction categorized after the snapshot was taken (by the user, a
sibling rule, or a concurrent AI backfill) was therefore silently
re-categorized to the rule's target and stamped category_source=Rule,
reverting a choice the user never asked the rule to touch.
Re-filter the set through shouldSkipForOnlyUncategorized() inside
applyRuleActionsToTransactions() when only_uncategorized was requested,
threading the flag through the job constructor. Skipped transactions are
no longer counted as changed.
The categorization, bulk rule re-evaluation, and automation-rule apply
status endpoints looked jobs up by a bare job UUID. Any authenticated
user who obtained another user's job id could poll its progress payload.
Include the owning user's id in the cache key so a status request keyed
by the polling user's id can only resolve that user's own jobs; a
mismatched owner now falls through to the existing 404. No ownership
store or extra lookup needed. Added cross-user isolation tests for all
three endpoints.
## Summary
- add creditor/debtor fields to transactions with raw_data backfill
- store counterparties on bank sync and CSV/XLS imports
- add creditor/debtor filters plus hidden table columns
## Tests
- php artisan test --compact tests/Feature/LocalizationTest.php
tests/Feature/OpenBanking/TransactionSyncServiceTest.php
tests/Feature/TransactionFilterTest.php
- npm test -- resources/js/lib/file-parser.test.ts --run
- vendor/bin/pint --dirty --format agent
Note: `npm run types` still has pre-existing unrelated errors; no
creditor/debtor/import-related errors remained in filtered output.
## Screenshot
<img width="1241" height="676" alt="8odYWtFcvUM"
src="https://github.com/user-attachments/assets/55653485-d588-4beb-9e6a-5c7c81ba7cf8"
/>
## Sentry issue
- PHP-LARAVEL-2F: https://whisper-money.sentry.io/issues/122581787/
## Root cause
- Automation rule match preview eagerly loaded account, bank, category,
and labels for every 500-transaction chunk, even for rules that only
inspect description fields.
- Sentry flagged repeated account/bank eager-load queries across chunks
as an N+1 pattern on
`/settings/automation-rules/{automationRule}/matches`.
## Fix
- Detect variables used by an automation rule and eager load only
relationships required for evaluation.
- Keep label eager loading only when label-only skip logic needs it.
- Avoid lazy loading unused relationships while preserving full data
shape for rule evaluation.
- Update the Sentry prompt to use the `sentry` CLI workflow.
## Verification
- `vendor/bin/pint --dirty --format agent`
- `php artisan test --compact
tests/Feature/AutomationRuleApplicationTest.php`
- `php artisan test --compact
tests/Feature/AutomationRuleEvaluationTest.php
tests/Feature/AutomationRuleApplicationTest.php`