Critical finding: transaction_details is NOT a legacy alias for transaction_name - they are distinct filters querying different database columns (entries.name vs transactions.extra JSONB). Merging as-is would silently break existing rules. https://claude.ai/code/session_01SnEJvb74QCgZJRbp791WJd
3.0 KiB
PR #1237 Review: Normalize legacy rule condition types
Critical Issue: transaction_details is NOT a legacy alias for transaction_name
This PR treats transaction_details as a legacy/renamed version of transaction_name, but they are semantically distinct filters that query entirely different data:
| Filter | Database Field | Purpose |
|---|---|---|
transaction_name |
entries.name (text column) |
Matches against the user-facing transaction name |
transaction_details |
transactions.extra (JSONB column) |
Matches against provider metadata (SimpleFIN payee, description, memo, etc.) |
Both filters are actively registered in Rule::Registry::TransactionResource#condition_filters (app/models/rule/registry/transaction_resource.rb). This means transaction_details is a current, functional filter — not a legacy type.
Impact of this PR as-is:
-
The migration (
20260321003133) would silently convert all existingtransaction_detailsconditions totransaction_name, breaking any rules that rely on matching provider metadata in theextraJSONB field. Users' rules would start matching againstentries.nameinstead — a completely different column with different data. -
The
before_validationcallback inRule::Conditionwould prevent anyone from ever creating atransaction_detailscondition again, even though the filter class still exists and is registered. -
The registry fallback in
get_filter!would silently redirect lookups fortransaction_detailsto thetransaction_namefilter, masking the issue.
Regarding "name" → "transaction_name"
Mapping legacy "name" to "transaction_name" seems reasonable — there is no separate name filter registered, and this appears to be a genuine rename. However, I'd recommend:
- Adding a test that confirms
"name"is not a registered filter key - Only normalizing
"name", not"transaction_details"
Code duplication
The legacy mapping logic (case statement) is duplicated across three locations:
Rule::Condition#normalize_condition_typeRule::Registry#get_filter!- The migration SQL
Extract this to a shared constant, e.g.:
# In Rule::Condition or a shared concern
LEGACY_CONDITION_TYPES = { "name" => "transaction_name" }.freeze
Recommendations
- Remove
"transaction_details"from the mapping entirely — it is a distinct, active filter - Keep only the
"name"→"transaction_name"normalization if there is evidence of legacy"name"values in production data - Add tests covering the normalization behavior and ensuring
transaction_detailsconditions continue to work - Extract the mapping to a single constant to avoid duplication
- Make the migration reversible or at minimum scope it to only normalize
"name"values
Summary
Requesting changes. The "name" normalization is fine, but including "transaction_details" would silently break existing rules by changing what data they match against. This needs to be separated out before merging.