Skip to content

2026-09-12: The Birthdays That Mattered

Pencil sketch on a dark textured chalkboard-like background of a warm library desk with an open leather-bound personal journal displaying handwritten names and dates of close friends and family, subtle warm golden candlelight, quiet mechanical clock, cozy vintage architectural drawing style

At 20:00 EDT on September 12, 2026, Jocasta delivered her scheduled evening intelligence briefing over Signal. Right alongside actual household calendar events—a cycliste grand prix and an appointment—sat three unexpected entries:

📅 Upcoming:
• tomorrow — Human Being Max Kit's Birthday (age 43)
• tomorrow — Mario Limperis's Birthday (age 49)
• tomorrow — Max Kit's Birthday (age 43)
• tomorrow at 9:00 AM - 10:00 AM — Grand prix cycliste
• tomorrow at 12:20 PM - 1:20 PM — RDV Harina

The briefing had succumbed to noise. Two problems were immediately apparent:

  1. Casual acquaintance flood: Bert did not need alerts for Max Kit or Mario Limperis, whose contacts had been swept from historic address book imports.
  2. Duplicate pollution: Max Kit appeared twice—once under Human Being Max Kit (a corporate-prefixed contact shell) and once as Max Kit. Because Jocasta limits upcoming items to eight entries, noisy birthdays were crowding out legitimate commitments.

Tracing the pipeline in jocasta-preflight.py revealed the source: icalBuddy was invoked with no calendar exclusions:

cmd = ["icalBuddy", "-n", "-nc", "-b", "", "-iep", "title,datetime,location", "-eep", "notes,attendees,url", rng]

On macOS, the built-in Calendar subsystem synthesizes events for all contacts in AddressBook under a synthetic Birthdays calendar. Furthermore, synced Exchange accounts maintain an additional Birthdays1 calendar. Querying without exclusion pulled every contact across more than 4,000 address book records, generating duplicate entries whenever a contact existed across both stores.

The solution required cleaning the source of truth in Sanctum CRM rather than patching superficial regexes.

1. Contact Deduplication (crm-make-pristine.py)

Section titled “1. Contact Deduplication (crm-make-pristine.py)”

While deal deduplication had previously landed in crm-make-pristine.py, contact records remained un-deduplicated across five disparate ingest streams (Affinity, Apple Contacts, iMessage, WhatsApp, Signal).

We extended crm-make-pristine.py:

  • Name Normalization: Strips honorifics and organizational artifacts (e.g. Human Being Max Kit normalizes to max kit).
  • Deterministic Keeper Ranking: Priority ranks entries by Affinity deal identity, presence of birthday, presence of email, and overall populated field count.
  • Field Merging: Any non-empty attribute on duplicate records missing on the keeper (such as phone numbers, emails, or birthdays) is copied to the keeper before archiving.
  • Reversible Archival: Duplicates are marked archived_reason = 'dup-contact' rather than deleted, respecting the master undo ledger in ~/.openclaw/workspace/crm-undo/.

Running the upgraded pristine pass yielded:

  • 1,444 duplicate contact entries archived.
  • 3,149 attribute fields merged onto keeper records.
  • 0 remaining duplicate contact groups in v_contact.

2. Close Friends & Family Tiering & Ground Truth Curation

Section titled “2. Close Friends & Family Tiering & Ground Truth Curation”

We established a canonical roster at ~/.sanctum/crm/close_friends_family.yaml (with example in sanctum-crm/config/close_friends_family.example.yaml), curated to twenty-seven close friends and immediate family members.

In optimization passes, we eliminated remaining gaps across the database:

  • Unicode Accent & Hyphen Folding: French names differing only by diacritics (e.g. Simon Fortin-Deschênes vs Simon Fortin-Deschenes, Frédéric Laurin vs Frederic Laurin) were unified. Keeper records were scored to favor accented typography, upgrading 25 keeper names and archiving 121 duplicates while merging 446 fields.
  • Ground Truth Family Verification: Corrected initial heuristic oversights:
    • Marthe Bouthillier: 0000-01-30 (Mother; verified via annual family messages).
    • Catherine Nepveu: 0000-11-24 (Sister; verified via Family calendar recurring event and SMS).
    • Lise Phénix: 1947-07-16 (Mother-in-law).
    • Andréanne Charbonneau: 1978-06-09 (Spouse).
    • Albert Nepveu: 2010-07-30 (Son; turning 17 in 2027).
    • Jean-Pierre Nepveu: 0000-09-26 (Father).
  • Pruning Extended Relatives: Unverified names swept in by initial %nepveu% queries (Normand, Simon, Louis, Maxime, Marie-Edith, Marie-Hélène, André Charbonneau, Charles Nepveu) were pruned from the roster and untagged in DuckDB.
  • Close Friends Discovery: Verified birthdays for Godot Huard (05-29), Guillaume Thérien (09-03), Vincent Dostie (10-12), Patrick Byrne (10-20), Martin Forget (1977-05-19), and Simon Fortin-Deschênes (02-03). Total verified close circle birthdays: 28 (6 family, 22 close friends).

In jocasta-preflight.py:

  1. icalBuddy now runs with -ec "Birthdays,Birthdays1", cutting off the 4,000-contact AddressBook firehose entirely.
  2. get_close_birthdays() queries v_contact in DuckDB, retrieving upcoming birthdays strictly for contacts tagged as Family or Close Friend.
  3. Dates are resolved with accurate age calculation for full birth years and clean name formatting for year-zero entries.

Added sanctum-crm birthdays [--days N] [--all] [--json] to inspect upcoming birthdays across the close circle at a glance.

Re-running jocasta-preflight.py and checking the composed briefing confirmed the fix:

📅 Upcoming:
• tomorrow at 9:00 AM - 10:00 AM — Grand prix cycliste
• tomorrow at 12:20 PM - 1:20 PM — RDV Harina

Casual acquaintances are gone. Duplicates are gone. And when Jean-Pierre Nepveu’s birthday arrives on September 26, it will surface with perfect clarity.

All unit tests pass:

  • sanctum-crm: 272/272 tests passed (including contact deduplication and birthdays CLI).
  • jocasta-heartbeat: 8/8 tests passed.