-- Record which rule a run came from. -- -- rule_apply_runs stored only counts ("13 matches · 13 transactions"), which is -- not enough to decide whether to revert: you cannot tell a merchant rename from -- a 50/50 split of your entire history. The snapshot column already holds the -- before-state, but nothing said what was applied or why. -- -- rule_name is denormalised deliberately. A run must stay readable after the -- rule it came from is edited or deleted -- the history is a record of what -- happened, not a pointer to what the rule says today. ALTER TABLE rule_apply_runs ADD COLUMN IF NOT EXISTS rule_id INTEGER, ADD COLUMN IF NOT EXISTS rule_name TEXT, -- 'all' = bulk run over every enabled rule; 'rule' = one rule by conditions; -- 'selection' = one rule against hand-picked transactions (preview → apply). ADD COLUMN IF NOT EXISTS source TEXT; ALTER TABLE rule_apply_runs DROP CONSTRAINT IF EXISTS rule_apply_runs_source_check; ALTER TABLE rule_apply_runs ADD CONSTRAINT rule_apply_runs_source_check CHECK (source IS NULL OR source IN ('all', 'rule', 'selection')); -- No FK to rules: deleting a rule must not cascade away the audit trail. CREATE INDEX IF NOT EXISTS idx_rule_apply_runs_rule ON rule_apply_runs (rule_id) WHERE rule_id IS NOT NULL; -- Existing rows stay NULL. There is no way to recover which rule they ran; -- the UI shows them as "unknown rule" rather than guessing.