#3597·sure

Feature: show automatic-categorization provenance on transactions

Author: joshiarnavCreated Sep 16, 2026Updated Sep 16, 2026

Summary

Sure already records who set a transaction's category: DataEnrichment(enrichable: transaction, attribute_name: "category_id", source: "ai" | "bayes" | "rule" | …) (Enrichable#log_enrichment). Since #3465 the auto-categorize flow first tries the local naive-Bayes classifier (source: "bayes") and only then the LLM (source: "ai"). None of this is visible in the Transactions UI, so after running auto-categorization users cannot tell which categories were chosen for them, or which of those they have since overridden.

Proposed UI

Transaction row (next to the existing Pending / Split / Possible duplicate pills, using DS::Pill):

  • AI — an ai/bayes category enrichment exists and its recorded value equals the current category_id. Tooltip names the source: "Categorized by AI" / "Categorized by the local Bayes classifier".
  • AI history — an ai/bayes category enrichment exists but the current category differs (user, rule or import changed it afterwards). Tooltip: "Was auto-categorized, then changed".
  • Nothing when there is no automatic category enrichment.

Transaction drawer (transactions/show): a compact read-only block in the Overview section, e.g.

Auto-categorization                          [AI]
Assigned "Groceries" by AI 2 hours ago.
Auto-categorization                          [AI history]
Assigned "Groceries" by the Bayes classifier 3 days ago; the category was changed afterwards.

Implementation notes

  • Data source is the existing DataEnrichment rows; no new table.
  • Add a scoped association on Transaction for the category_id enrichments from automatic sources so the index can includes it (no per-row query). Provenance logic lives in a small PORO/model method, not in ERB.
  • "Current" wins when both an ai and a bayes row exist and either matches the current category; otherwise "history". Show the most recent matching row in the drawer.
  • Localize all strings; design tokens only.

Acceptance criteria

  • Current auto-assignment → AI pill; changed afterwards → AI history; no enrichment → no pill.
  • Drawer shows assigned category name, source, relative time, and whether it is still current.
  • Category deleted since → drawer degrades gracefully ("a category that no longer exists").
  • No N+1 on the Transactions index (query-count assertion).
  • Semantics identical to the companion filter feature.