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
ParserRulecarries a stableid(e.g.mae_payment_of) that is now persisted asrules.sourceId(nullable, unique index, added in schema v5). - On an existing install with a stored
rules_seed_versionbelowRULE_SEED_VERSION, the repository: - backfills
sourceIdonto legacy rows by matchinglabel(a one-time migration of seed identity), and - inserts any starter rule whose
sourceIdis absent — this is how new rules reach existing installs. - The reconcile writes only with in-place
UPDATE(backfill) andINSERT(new rules) — neverREPLACE— so thetransactions.ruleId → rules.id ON DELETE SET NULLFK is never triggered and rule provenance (ADR-0002) survives. - The version stamp is stored in the
settingstable (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 fromsourceId ?: 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
labelonly, no schema change — rejected: labels are human-readable and editable-adjacent;sourceIdis 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.