This repository was archived by the owner on Jul 15, 2026. It is now read-only.
Settings UI for contact merge rules and manual merge/split - #31
Merged
Conversation
The merge engine (#11) can split identifiers off a contact but exposes no way to enumerate the multi-identifier contacts a split would act on. MergedContacts returns every contact holding at least two identifiers, each with its identifiers ordered by source then value and the set ordered by display name — exactly the review set the Settings → Contacts tab (#12) lists so a mistaken merge can be pulled apart. Single-identifier contacts (nothing to split) are excluded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The user-facing surface over the cross-provider contact merge engine (#11, ADR-0022 / SPEC-0015), the seventh Settings sub-nav tab (#12): - Matching RULES form: which identifier kinds to trust (phone/email), whether to auto-merge or only suggest, and whether the native address book contributes hint suggestions — persisted via SetMergeRules / loaded via GetMergeRules. - CANDIDATE review: MergeCandidates lists the suggested cross-source merges under the current rules, each with a one-click Merge (MergeContacts). - SPLIT overrides: the merged (multi-identifier) contacts, each with a per-identifier checklist to pull a mistakenly-merged person apart (SplitContact). Both overrides record durable decisions in the engine, so they survive re-ingest. The address-book toggle mirrors the resolver's tri-state (#9 seam): live only when contacts.Available; disabled with the matching absent / needs-permission affordance otherwise, and a save from the disabled control never flips the stored preference. Works today against the Unavailable no-op on main; the macOS provider (#10) lands in parallel. Every mutating POST (save-rules / merge / split) is gated by the same checkSetupPOST contract as the Setup POSTs (same-origin + per-session token + body cap, 403 before any work) and re-renders the boosted partial with a fixed-enum result banner, mirroring the PairResult/UnpairResult pattern. No inline JS (CSP script-src 'self'); the toggle-chip disabled styling is added to input.css and app.css regenerated. Re-bumps the settings-tab count assertion (6 → 7) after #2's Backups tab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the address-book resolver is unavailable, handleSettingsMergeRules preserves the stored UseAddressBook preference by reading it back before persisting. A read error was swallowed, leaving UseAddressBook at its zero value (false) and then persisting it — silently clearing the user's stored preference. Treat the read error as a save failure: log it and re-render the error banner instead of writing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #8. Closes #12.
The user-facing control surface over the merge engine (#11), driving the ContactResolver seam (#9). Depends only on merged interfaces, not the parallel #10 branch — the address-book-hint toggle works against the
Unavailableno-op on main today.What
internal/store/contactmerge.go):MergedContacts(ctx)+MergedContact— enumerates contacts holding ≥2 identifiers (the review set the split UI acts on).handleSettingsContacts(GET/settings/contacts),handleSettingsMergeRules(POST/settings/contacts/rules),handleSettingsMerge(POST/settings/contacts/merge),handleSettingsSplit(POST/settings/contacts/split).contact_settings.html: three cards — Matching rules (toggle-chip checkboxes: match_phone / match_email / use_address_book / auto_merge, persisted viaSetMergeRules), Suggested merges (MergeCandidates, one-click Merge), Merged contacts (MergedContacts, Split).PairResult/UnpairResultpattern; CSP-safe (no inline JS); boosted partial owning<title>(SPEC-0008).contactResolver().Availability(ctx)is notAvailable(Linux / permission denied), the address-book-hint toggle renders disabled, consistent with the existing setup permission surfacing.Coordination
Re-bumps the
settings_subnavtab-count assertion insettings_shell_test.go(from the 6 that #2's Backups tab set → 7).app.cssregenerated with the Tailwind toolchain and verified CI-fresh.Tests
Web-layer: rules-form round-trip, a merge action, a split action, the disabled address-book state under the
Unavailableresolver, and the boosted-partial contract; plusTestMergedContactsListsMultiIdentifierOnlyat the store layer.🤖 Generated with Claude Code
Generated by Claude Code