Skip to content
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
joestump-agent merged 3 commits into
mainfrom
feature/12-merge-settings-ui
Jul 11, 2026
Merged

Settings UI for contact merge rules and manual merge/split#31
joestump-agent merged 3 commits into
mainfrom
feature/12-merge-settings-ui

Conversation

@joestump-agent

Copy link
Copy Markdown
Owner

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 Unavailable no-op on main today.

What

  • Store (internal/store/contactmerge.go): MergedContacts(ctx) + MergedContact — enumerates contacts holding ≥2 identifiers (the review set the split UI acts on).
  • Web — new Settings → Contacts tab (the 7th sub-nav tab):
    • 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 via SetMergeRules), Suggested merges (MergeCandidates, one-click Merge), Merged contacts (MergedContacts, Split).
    • Fixed-enum POST-result banners mirroring the PairResult/UnpairResult pattern; CSP-safe (no inline JS); boosted partial owning <title> (SPEC-0008).
  • Platform state: when contactResolver().Availability(ctx) is not Available (Linux / permission denied), the address-book-hint toggle renders disabled, consistent with the existing setup permission surfacing.

Coordination

Re-bumps the settings_subnav tab-count assertion in settings_shell_test.go (from the 6 that #2's Backups tab set → 7). app.css regenerated 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 Unavailable resolver, and the boosted-partial contract; plus TestMergedContactsListsMultiIdentifierOnly at the store layer.

🤖 Generated with Claude Code


Generated by Claude Code

claude added 3 commits July 11, 2026 13:56
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>
@joestump-agent
joestump-agent merged commit 1d02a4b into main Jul 11, 2026
3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Contact merging: settings UI for merge rules and manual overrides

2 participants