Skip to content

fix(config): store mailing-list addresses in dedicated Contact.Addresses slice#1277

Open
mvanhorn wants to merge 2 commits into
floatpane:masterfrom
mvanhorn:osc/1123-mailing-list-addresses-field
Open

fix(config): store mailing-list addresses in dedicated Contact.Addresses slice#1277
mvanhorn wants to merge 2 commits into
floatpane:masterfrom
mvanhorn:osc/1123-mailing-list-addresses-field

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

What?

SearchContacts builds mailing-list virtual contacts by jamming strings.Join(list.Addresses, ", ") into Contact.Email. That breaks the field's single-address invariant: normalizeContactEmail lowercases and trims commas off the joined string (corrupting the data), and any exact-match lookup against the contact will never find it.

This adds a dedicated Addresses []string field to Contact, populates it for mailing-list virtual contacts, and leaves Email empty so the invariant holds. The composer learns to detect mailing lists via len(selected.Addresses) > 0 instead of "does the email field contain a comma", joins the addresses at insertion time, and renders the suggestion as Name (addr1, addr2, ...) so it's clear that a single suggestion expands to multiple recipients.

Files touched:

  • config/cache.go -- new Addresses field on Contact; mailing-list construction in SearchContacts switches to it
  • tui/composer.go -- detection/insertion and rendering swap from Email commas to Addresses

The cache file format is backwards-compatible: the new field is omitempty, and saved contacts (added via AddContact) never carry Addresses because they're built from a single email parameter. Mailing-list virtual contacts are constructed in-memory on every search and never written to disk.

Why?

Closes #1123. The reporter pointed out the data-integrity bug; this implements option 2 of the two fixes they suggested (dedicated Addresses slice, keep Email empty), which is the smaller of the two and doesn't require a separate "virtual contact" type.

Verification

  • go build ./config/... ./tui/...: passes
  • go vet ./config/... ./tui/...: passes
  • go test ./config/...: 51/51 passing
  • go test ./tui/...: 79/79 passing

Disclosure

Authored with Claude as a coding assistant; I reviewed the change before pushing.

…ses slice

SearchContacts emitted mailing-list virtual contacts with the joined
"a@x.com, b@y.com" string crammed into Contact.Email, breaking the
single-address invariant. Any downstream code that calls
normalizeContactEmail lowercased and trimmed commas off the joined
string, corrupting the data, and exact-match lookups against the
contact would never find it.

Add an Addresses []string field to Contact and populate it for mailing
lists; leave Email empty so the single-address invariant holds. Detect
mailing lists in the composer via len(Addresses) > 0 instead of "does
the email field contain a comma". Render mailing-list suggestions as
"Name (addr1, addr2, ...)" so it's clear they expand to multiple
recipients.

Fixes floatpane#1123
@mvanhorn mvanhorn requested a review from a team as a code owner May 12, 2026 08:42
@floatpanebot floatpanebot added the size/S Diff: 11–50 lines label May 12, 2026
Copy link
Copy Markdown
Member

@floatpanebot floatpanebot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mvanhorn! Please fix the following issues with your PR:

  • Title: Is too long (78 characters). The PR title must be strictly under 40 characters.

@floatpanebot floatpanebot added area/tui Terminal UI / view layer area/config Configuration / settings bug Something isn't working ci CI / build pipeline labels May 12, 2026
…list-addresses-field

# Conflicts:
#	config/cache.go
Copy link
Copy Markdown
Member

@floatpanebot floatpanebot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mvanhorn! Please fix the following issues with your PR:

  • Title: Is too long (78 characters). The PR title must be strictly under 40 characters.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Resolved and pushed a29f108. The conflict was structural -- since this PR opened, master refactored Contact to track usage per-account (Usage map[string]ContactUsage with a legacyContactUsageKey migration path), and that bumped into the new Addresses []string field here.

Merged shape:

type Contact struct {
    Name      string                  `json:"name"`
    Email     string                  `json:"email"`
    Addresses []string                `json:"addresses,omitempty"`
    Usage     map[string]ContactUsage `json:"usage_by_account"`
}

Mailing-list virtual contacts in SearchContacts now leave Email empty, copy addresses into Addresses, and set the 9999 usage score under the active account in the Usage map. The composer's len(selected.Addresses) > 0 check stays as-is and is the canonical way to distinguish mailing-list vs regular contacts.

go build ./..., go vet ./..., and go test ./config/... ./tui/... ./cli/... all clean locally. Branch shows MERGEABLE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Configuration / settings area/tui Terminal UI / view layer bug Something isn't working ci CI / build pipeline size/S Diff: 11–50 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Mailing-list virtual contact stores joined addresses in Email field

2 participants