Skip to content

fix: mailing-list addresses in a slice#1277

Merged
andrinoff merged 3 commits into
floatpane:masterfrom
mvanhorn:osc/1123-mailing-list-addresses-field
May 16, 2026
Merged

fix: mailing-list addresses in a slice#1277
andrinoff merged 3 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.

@andrinoff andrinoff changed the title fix(config): store mailing-list addresses in dedicated Contact.Addresses slice fix: store mailing-list addresses in a slice May 13, 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 @andrinoff! Please fix the following issues with your PR:

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

@andrinoff andrinoff changed the title fix: store mailing-list addresses in a slice fix: mailing-list addresses in a slice May 13, 2026
@floatpanebot floatpanebot dismissed stale reviews from themself May 13, 2026 12:57

Formatting issues have been resolved. Thank you!

Copy link
Copy Markdown
Member

@andrinoff andrinoff left a comment

Choose a reason for hiding this comment

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

Fix the issue below, and add tests

Comment thread tui/composer.go Outdated
display := s.Email
if s.Name != "" && s.Name != s.Email {
if len(s.Addresses) > 0 {
display = fmt.Sprintf("%s (%s)", s.Name, strings.Join(s.Addresses, ", "))
Copy link
Copy Markdown
Member

@andrinoff andrinoff May 13, 2026

Choose a reason for hiding this comment

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

fmt.Sprintf("%s (%s)", s.Name, strings.Join(s.Addresses, ", ")) for a mailing list with many recipients could produce a very long suggestion string and overflow the TUI layout. Let's truncate

The unbounded `fmt.Sprintf("%s (%s)", s.Name, strings.Join(s.Addresses, ", "))`
could produce a multi-line string for mailing lists with many recipients and
overflow the TUI suggestion box.

Extract a `suggestionDisplay` helper that truncates the mailing-list branch
to the suggestion box width (composer width minus padding, with a 40-rune
fallback when width is unset). Single-address suggestions are unchanged.
Truncation is rune-safe and adds an ellipsis when the budget allows.

Adds `TestMailingListSuggestionTruncates` covering both the truncated
mailing-list case and the still-full single-address case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@floatpanebot floatpanebot added the size/M Diff: 51–200 lines label May 16, 2026
@mvanhorn
Copy link
Copy Markdown
Contributor Author

Addressed in 14a67a6 — extracted a suggestionDisplay() helper that truncates the mailing-list branch to the composer's available width (width - 6, 40-rune fallback). Truncation is rune-safe and adds an ellipsis. Single-address suggestions are untouched.

Added TestMailingListSuggestionTruncates covering both the truncated mailing-list case and the unchanged single-address case. go build ./... and go test ./tui/... are clean.

Copy link
Copy Markdown
Member

@andrinoff andrinoff left a comment

Choose a reason for hiding this comment

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

lgtm

@andrinoff andrinoff merged commit 4876a93 into floatpane:master May 16, 2026
14 checks passed
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/M Diff: 51–200 lines 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

3 participants