import: a maiden name is written under the note
Assisted-by: GLM-5.3 via pi
change lwpxyooyxulzrmtwxttrwvvzvuxmnxrq
commit 5136ebc6e6c647c88ac2d329b15f89c4f22d789b
author Alpha Chen <alpha@kejadlen.dev>
date
parent uwsqtxww
diff --git a/lib/pro_tacts/import/macos.rb b/lib/pro_tacts/import/macos.rb
index e74abe2..47e6146 100644
--- a/lib/pro_tacts/import/macos.rb
+++ b/lib/pro_tacts/import/macos.rb
@@ -123,6 +123,7 @@ module ProTacts
         first, last = name(carried, source_id, unknown)
         nickname = only(carried, "NICKNAME", source_id, unknown)
         birthday = only(carried, "BDAY", source_id, unknown)
+        maiden_name = only(carried, "X-MAIDENNAME", source_id, unknown)
         labels = labels_by_group(lines)
         phones = properties(carried, "TEL").filter_map { |property|
           number = property.text
@@ -143,19 +144,23 @@ module ProTacts
         photo = !contact.fetch("imageData").nil?
         card = Card.new(
           first:, last:, nickname:, birthday:, phones:, emails:, addresses:,
-          note: noted(note, related_names(lines, carried)), photo:, groups: [], source:
+          note: noted(note, maiden_name, related_names(lines, carried)), photo:, groups: [], source:
         )
         Plan::Entry.new(id:, source_id:, card:)
       end
 
-      # The note, with the people this contact is related to written
-      # under it: this address book has no field for a relation, and a
-      # spouse's name is worth more in the note than nowhere.
-      #: (String? note, Array[String] related) -> String?
-      def self.noted(note, related)
-        return note if related.empty?
-
-        [note, related.join("\n")].compact.reject(&:empty?).join("\n\n")
+      # The note, with the maiden name and the people this contact is
+      # related to written under it: this address book has no field for
+      # either, and a name worth keeping is worth more in the note than
+      # nowhere (the related names' reasoning,
+      # docs/plans/2026-09-16-importing-from-macos.md, "The builder
+      # knows only what it was taught").
+      #: (String? note, String? maiden, Array[String] related) -> String?
+      def self.noted(note, maiden, related)
+        under = [maiden&.then { "Maiden name: #{it}" }, *related].compact
+        return note if under.empty?
+
+        [note, under.join("\n")].compact.reject(&:empty?).join("\n\n")
       end
 
       # Each related name as "<label>: <name>", the label being the
@@ -329,7 +334,8 @@ module ProTacts
           "item#.EMAIL;type=INTERNET"
         ],
         "ADR" => ["ADR;type=HOME", "item#.ADR;type=HOME"],
-        "X-ABRELATEDNAMES" => ["item#.X-ABRELATEDNAMES"]
+        "X-ABRELATEDNAMES" => ["item#.X-ABRELATEDNAMES"],
+        "X-MAIDENNAME" => ["X-MAIDENNAME"]
       }.freeze #: Hash[String, Array[String]]
 
       # A number as Contacts hands one over, which is as a person typed
diff --git a/test/pro_tacts/import/test_macos.rb b/test/pro_tacts/import/test_macos.rb
index 597b9c2..56f9f94 100644
--- a/test/pro_tacts/import/test_macos.rb
+++ b/test/pro_tacts/import/test_macos.rb
@@ -311,6 +311,47 @@ class ImportMacosTest < Minitest::Test
     end
   end
 
+  def test_a_maiden_name_is_written_under_the_note
+    in_tmpdir do |dir|
+      lines = [
+        "X-MAIDENNAME:Byron",
+        "item2.X-ABRELATEDNAMES;type=pref:Sylvia Lovelace",
+        "item2.X-ABLabel:_$!<Spouse>!$_"
+      ]
+      plan = Macos.plan(dir, [record("A:ABPerson", lines:, note: "Analyst.")], created_at: CREATED_AT)
+
+      assert_equal "Analyst.\n\nMaiden name: Byron\nSpouse: Sylvia Lovelace", plan.card(plan.contacts.first.id).note
+    end
+  end
+
+  def test_a_maiden_name_is_the_whole_note_of_a_contact_with_none
+    in_tmpdir do |dir|
+      plan = Macos.plan(dir, [record("A:ABPerson", lines: ["X-MAIDENNAME:Byron"])], created_at: CREATED_AT)
+
+      assert_equal "Maiden name: Byron", plan.card(plan.contacts.first.id).note
+    end
+  end
+
+  def test_a_second_maiden_name_is_refused
+    in_tmpdir do |dir|
+      records = [record("A:ABPerson", lines: ["X-MAIDENNAME:Byron", "X-MAIDENNAME:King"])]
+
+      error = assert_raises(Macos::Unknown) { Macos.plan(dir, records, created_at: CREATED_AT) }
+
+      assert_includes error.message, "X-MAIDENNAME lines: 2: 1"
+    end
+  end
+
+  def test_a_maiden_name_in_any_other_form_is_refused_under_that_form
+    in_tmpdir do |dir|
+      records = [record("A:ABPerson", lines: ["item1.X-MAIDENNAME:Byron"])]
+
+      error = assert_raises(Macos::Unknown) { Macos.plan(dir, records, created_at: CREATED_AT) }
+
+      assert_includes error.message, "item#.X-MAIDENNAME: 1"
+    end
+  end
+
   def test_an_annotation_with_no_line_to_annotate_is_refused
     in_tmpdir do |dir|
       records = [record("A:ABPerson", lines: ["item5.X-ABADR:us", "X-ABLabel:_$!<Spouse>!$_"])]