Skip to content

ADR-0019: Starter-rule re-seeding for existing installs

  • Status: Accepted
  • Date: 2026-08-13
  • Deciders: maintainers
  • Related: ADR-0004

Context

Starter parse rules live in BankRuleSets and are seeded into the rules table on first launch (ADR-0004). Rules shipped in a later release therefore never reached existing installs — the table was only populated when empty, and there is deliberately no static fallback. When mae_payment_of and tng_transferred_to_you were added (issue #20) they had to reach users who already had a populated rules table.

Decision

RuleRepository.seedDefaults() seeds on first launch, then reconciles once per seed version:

  • Each ParserRule carries a stable id (e.g. mae_payment_of) that is now persisted as rules.sourceId (nullable, unique index, added in schema v5).
  • On an existing install with a stored rules_seed_version below RULE_SEED_VERSION, the repository:
  • backfills sourceId onto legacy rows by matching label (a one-time migration of seed identity), and
  • inserts any starter rule whose sourceId is absent — this is how new rules reach existing installs.
  • The reconcile writes only with in-place UPDATE (backfill) and INSERT (new rules) — never REPLACE — so the transactions.ruleId → rules.id ON DELETE SET NULL FK is never triggered and rule provenance (ADR-0002) survives.
  • The version stamp is stored in the settings table (rules_seed_version). Deleting a starter rule in the editor stays deleted: the reconcile only runs when we ship new rules and bump the version.

Consequences

  • New starter rules reach existing installs on the next launch after upgrade.
  • User-edited rules are never overwritten; user-deleted starter rules are never re-added (once the version is stamped).
  • toParserRule() now derives its id from sourceId ?: label, so provenance and the seed identity stay stable across the label-vs-id distinction.
  • Adding rules requires bumping RULE_SEED_VERSION; forgetting to bump means the new rules only reach fresh installs (the old behavior).

Alternatives considered

  • Reconcile by label only, no schema change — rejected: labels are human-readable and editable-adjacent; sourceId is the stable key and also fixes the label-as-id smell.
  • Reconcile unconditionally on every launch — rejected: would re-add user-deleted starter rules; the version stamp bounds it to actual releases.