Stage B step 2 (service layer; HTTP handlers next).
Rename rewrites NormalizedName along with DisplayName. Leaving the key
stale would show the new name while still matching the old one, so the
archive would look correctly configured while importing a different shop.
Mutation-tested: updating only display_name makes the rename test fail.
Duplicates are reported against the name already stored, not the one just
submitted — the two can differ only in case or character width, and
echoing back what was typed reads as the system rejecting a name it does
not have.
Delete is a soft delete carrying the id into DeletedFlag, so the same
name can be added again afterwards while past sync records keep resolving
the old row.
EnabledNames returns an empty map without error. Empty is a legitimate
state that callers must turn into "refuse to sync", never "import
everything".
MarkSeen only updates shops already on the list. A sync must not grow the
allow-list as a side effect; discovery is a separate explicit action.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>