Skip to content

feat(leads): record contacts on both research paths, and let the list out - #153

Merged
ralyodio merged 1 commit into
masterfrom
feat/contacts-backfill-and-export
Jul 28, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/contacts-backfill-and-export

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor
outreach_contacts            9 rows
leads with an address       77
missing from contacts       70

A missing call, not a broken one

researchProspect has two branches. Only the unscanned one called recordContact. Almost every lead arrived through the scanning branch, which wrote the address onto the prospect and nowhere else — so the durable, org-scoped record the table exists to be was very nearly empty, while the data sat in project-scoped rows that have to be read one project at a time.

Both branches record now, and the scanning branch sets contact_id so the link works in both directions.

Contacts now carry a niche

They never did. The campaign that found someone is the only statement about them the pipeline can make without asking a model to guess — someone found by a campaign called "game designers" is a game designer. A list that can't be segmented is worth very little, and segment size is what decides that.

Backfill: the history the code fix can't reach

Idempotent — the unique index on (organization_id, identity_key) already holds every row it would add. Applied to prod:

contacts          9  →  80
with a niche             63
with a company           51
prospects linked         78
still missing             0

It had no surface at all

Which made it a table, not an asset. Now: a panel showing segment sizes, and a CSV export.

  • Fields starting with =, +, - or @ are prefixed — a contact list is exactly the kind of file opened in a spreadsheet without a thought, and a leading = there is executable
  • RFC 4180 quoting, CRLF, UTF-8 BOM so Excel doesn't mangle accented names
  • do_not_contact rows are excluded from both the counts and the export, so a downloaded file can't reintroduce them somewhere else
  • Viewers can read it — a read-only member being unable to open the list would make the export useless to the people most likely to want it

Checks

  • tsc --noEmit clean · 1,028 tests pass (14 new) · build compiles
  • Three of the new tests read the source: a unit test of upsertContact passed the whole time this was broken, because nothing called it

🤖 Generated with Claude Code

… out

outreach_contacts held nine rows against seventy-seven addressable leads.
The cause was a missing call rather than a broken one: researchProspect
has two branches, and only the unscanned one recorded a contact. Almost
every lead arrived through the scanning branch, which wrote the address
onto the prospect and nowhere else — so the durable, org-scoped record
the table exists to be was very nearly empty, while the data itself sat
in project-scoped prospect rows that have to be read one project at a
time.

Both branches record now, and the scanning branch links the prospect back
to the contact so the relationship works in both directions.

Contacts also carry a niche, which they never did. The campaign that
found someone is the only statement about them the pipeline can make
without asking a model to guess: someone found by a campaign called "game
designers" is a game designer. A list that cannot be segmented is worth
very little, and segment size is the thing that decides that.

The backfill migration is the history the code fix cannot reach —
everything found before it existed. Idempotent, since the unique index
already holds every row it would add. Applied: 9 contacts to 80, 63 with
a niche, 78 prospects linked, none left unrecorded.

And the table had no surface at all, which made it a table rather than an
asset. There is now a panel showing segment sizes and a CSV export.
Fields that begin with =, + or - are prefixed, because a contact list is
exactly the kind of file that gets opened in a spreadsheet without a
thought. Anyone who asked not to be contacted is excluded from the counts
and the export both, so a file cannot reintroduce them elsewhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 83a31e2 into master Jul 28, 2026
8 checks passed
@ralyodio
ralyodio deleted the feat/contacts-backfill-and-export branch July 28, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant