* Trace "Reset AI cache" runs in the debug log
The /rules "Reset AI cache" button fired a background job whose only
output was Rails.logger, so there was no way to tell from the app whether
a reset ran, partially failed, or never started.
Every stage now writes to DebugLogEntry under the new "ai_cache_reset"
category, so a whole run is filterable in /settings/debug:
- info when the request is enqueued from the rules page, and info again
when the job starts (a request with no matching start means the job
never reached a worker)
- error when a scope fails outright, or when the enqueue itself fails
- warn (capped at 5 per scope) for individual records that could not be
cleared, plus warn when the job is handed no family
- info on completion with the number of AI cache entries removed, broken
down by scope, with failures and skipped records in the metadata
The completion count needed fixing to be worth reporting: the class-level
Enrichable.clear_ai_cache counted records visited, not cache entries
removed, so it reported every transaction in the family regardless of
whether anything was cleared. It now sums the enrichments actually
deleted, and takes an optional block so a single unclearable record
warns and is counted instead of aborting the sweep and discarding the
tally of everything already cleared.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWwaCxjhSxBpvvgNLmCw67
* Treat a false perform_later result as an enqueue failure
perform_later turns an ActiveJob::EnqueueError — or an enqueue aborted by
a callback — into a false return rather than raising it, so the previous
rescue-only check missed those cases entirely: the controller logged the
reset as requested and redirected with a success notice while nothing had
been queued, which is exactly the blind spot this branch set out to close.
Branch on the return value and raise the job's own enqueue_error when it
carries one, so both failure modes route through the same error entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWwaCxjhSxBpvvgNLmCw67
* Cover the yielded enqueue_error path and assert the job argument
The false-return test stubs perform_later without yielding, so it only
exercised the fallback error. The branch that re-raises the job's own
enqueue_error — the one that carries the adapter's underlying cause into
the debug entry, which is the point of surfacing it at all — had no
coverage. Add a test that yields a job carrying an EnqueueError and
asserts the cause reaches both the raised error and the entry metadata.
Also assert the family is what gets enqueued, in all three tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWwaCxjhSxBpvvgNLmCw67
* Scope the enqueue rescue to the enqueue
The rescue reports "could not be enqueued", but it also covered the
request log that runs after the job is safely queued. That was harmless
in practice — DebugLogEntry.capture rescues internally and returns nil,
so it cannot raise — but the guarantee rested on the internals of a
different class rather than on the shape of this method.
Split the enqueue into its own method so the rescue covers only what it
reports on. Nothing after a successful enqueue can now be recorded as an
enqueue failure and retried, regardless of what those later steps call.
No behavior change on any of the four paths already covered by tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWwaCxjhSxBpvvgNLmCw67
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Add blocked count to rule run summary
* test(rules): cover rule run blocked counts
* fix(rules): derive blocked count from modified rows
Blocked rule transactions are the processed rows that were not modified. This keeps the displayed queued / processed / modified / blocked summary aligned when a run has already processed all matching rows but some were skipped by enrichment locks.
* fix(rules): count processed rows for rule jobs
Synchronous rule actions return the number of rows they modified, but rule-run processed counts should represent the number of matched transactions the job attempted to process. Using queued matches for processed preserves the distinction between processed and modified rows, which lets locked manual edits appear as blocked instead of making processed collapse to modified.
This changes RuleJob counter semantics, so it was committed separately from the derived blocked-count display change.
* fix: Handle empty compound conditions on rules index
* fix: avoid contradictory rule condition summary on /rules
* refactor: move rules condition display logic from view to model
* fix: localize rule title fallback and preload conditions in rules index
* correct dialog#close -> DS--dialog#close
* additional ds-- add
* stop reload on close
* Added option for reload_on_close var
* Fix test to allow query param in redirect URL
* Domain model sketch
* Scaffold out rules domain
* Migrations
* Remove existing data enrichment for clean slate
* Sketch out business logic and basic tests
* Simplify rule scope building and action executions
* Get generator working again
* Basic implementation + tests
* Remove manual merchant management (rules will replace)
* Revert "Remove manual merchant management (rules will replace)"
This reverts commit 83dcbd9ff0aa7bbee211796b71aa48b71df5e57e.
* Family and Provider merchants model
* Fix brakeman warnings
* Fix notification loader
* Update notification position
* Add Rule action and condition registries
* Rule form with compound conditions and tests
* Split out notification types, add CTA type
* Rules form builder and Stimulus controller
* Clean up rule registry domain
* Clean up rules stimulus controller
* CTA message for rule when user changes transaction category
* Fix tests
* Lint updates
* Centralize notifications in Notifiable concern
* Implement category rule prompts with auto backoff and option to disable
* Fix layout bug caused by merge conflict
* Initialize rule with correct action for category CTA
* Add rule deletions, get rules working
* Complete dynamic rule form, split Stimulus controllers by resource
* Fix failing tests
* Change test password to avoid chromium conflicts
* Update integration tests
* Centralize all test password references
* Add re-apply rule action
* Rule confirm modal
* Run migrations
* Trigger rule notification after inline category updates
* Clean up rule styles
* Basic attribute locking for rules
* Apply attribute locks on user edits
* Log data enrichments, only apply rules to unlocked attributes
* Fix merge errors
* Additional merge conflict fixes
* Form UI improvements, ignore attribute locks on manual rule application
* Batch AI auto-categorization of transactions
* Auto merchant detection, ai enrichment in batches
* Fix Plaid merchant assignments
* Plaid category matching
* Cleanup 1
* Test cleanup
* Remove stale route
* Fix desktop chat UI issues
* Fix mobile nav styling issues