Edit a rule from the rules screen

A rule could be written and deleted but never changed, so fixing a glob meant
deleting the rule and typing it again -- losing its note, the comments around
it, and its position, which still breaks ties between equally specific rules.
e now opens the selected rule in the builder and enter rewrites it where it
sits. config.ReplaceRule edits rules.toml textually, as deleting does, and
keeps the comments above the rule: they say why it is there, which editing its
glob rarely changes.

The round trip must not lose what the form does not show. The builder has four
fields and a Rule has five, so an edit carries the type pattern through
untouched and says so under the glob; a type-only rule saves without one. The
preview needed the same care in reverse: a working rule's transactions are
tagged, so an untagged-only preview would be empty for it. Its own rows are
added back, and the ones a narrowed glob stops catching stay on screen marked
-- giving one up is the decision being made, and it must not happen silently.

The builder and its list shared one return view, so opening the builder from
the list left esc pointing back into the form. Each screen now remembers its
own way out; transfers had the same trap and the same fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 19:40:55 +02:00
co-authored by Claude Opus 5
parent 442684be60
commit 6477147988
6 changed files with 739 additions and 105 deletions
+25 -6
View File
@@ -167,12 +167,31 @@ than a `#` comment so `LoadRules` can return it, the builder can write it and
the rules screen can show it. It never takes part in matching — `rules.Engine`
does not look at it — and it must stay that way.
**`config.DeleteRules` edits rules.toml textually, never by re-serialising the
parsed rules**, because comments and formatting are not recoverable from
`[]Rule`. A rule owns the comment lines directly above it; a comment separated
by a blank line is a heading for what follows and stays. Both writers go
through `writeFileAtomic`, and deletion re-parses the result before replacing
the file.
**`config.DeleteRules` and `config.ReplaceRule` edit rules.toml textually, never
by re-serialising the parsed rules**, because comments and formatting are not
recoverable from `[]Rule`. A rule owns the comment lines directly above it, so
deleting takes them with it; a comment separated by a blank line is a heading
for what follows and stays. Replacing keeps them — they say why the rule is
there, which editing its glob rarely changes — and rewrites only the rule's own
lines, leaving its *position* alone: position still breaks ties, so a rule that
moved could start beating an equally specific one it never used to. Every
writer goes through `writeFileAtomic`, and both editors re-parse the result
before replacing the file.
**An edit must not lose what the builder does not show.** The form has four
fields and a `Rule` has five, so `saveRule` carries `Type` through from the rule
being edited and the form says it is doing so. Nothing may round-trip a rule
through those four fields alone — a pattern the user was never shown is not a
pattern they chose to remove. A new field on `Rule` needs the same treatment or
a field of its own. Covered by `TestRuleEditKeepsTypePattern`.
**The rule builder's preview is what the rule is judged against, which is not
always "what is untagged".** For a new rule those are the same thing. For an
edit they are not: the rule's own transactions are tagged, so an untagged-only
preview would be empty for a rule that works. `reloadPreviewGroups` adds them
back, and `refreshRulePreview` keeps the ones the new glob stops catching on
screen marked `−` instead of dropping them silently, because giving one up is
the decision being made.
## Adding a bank parser