Skip to content

fix(leads): record the people a campaign names, not just the companies - #154

Merged
ralyodio merged 1 commit into
masterfrom
fix/person-mode-contacts
Jul 28, 2026
Merged

ralyodio merged 1 commit into
masterfrom
fix/person-mode-contacts

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor
outreach_contacts        80 rows
  ... with a name         0
  ... with a title        0
  ... with a linkedin     0

That looked like person extraction being broken. It wasn't — extraction works fine. The campaign runner ignored its output:

result.errors.push(...found.errors);
result.awaitingAuth = found.loginRequiredSeeds;
// found.people — never read

Discovery renders the page, walks the pagination, opens each entry and parses a name, title and profile link out of it. Then the runner took prospects and errors and dropped people on the floor — discarding every person after paying to find them.

Why it survived

The one-shot finder did it correctly. The path used to test the feature worked; the path used to run it didn't. So both now go through one shared recorder — two copies is what let them drift.

People found by a campaign also carry that campaign's niche, matching how prospect-route contacts are already labelled.

Verified against the directory that exposed it

people: 11   prospects: 1
Marc van Neerven — Chief Technology Officer
Blake Lindsay  — Chief Technology Officer
Cyril Bouthors — CTO
recorded: 11

In the database, with titles and niche attached:

full_name title niche
John Szeder Fractional CTO for early stage businesses CTOs
Bharat Kishnani Co-Founder and CTO CTOs
Daniel Buckton Chief Technology Officer CTOs

The names lost earlier can't be backfilled — they were never written down anywhere. Re-running a campaign recovers them.

Checks

  • tsc --noEmit clean · 1,036 tests pass (8 new) · build compiles
  • Three tests read the source and assert neither path calls upsertContact directly, so they can only drift again by deleting a test

🤖 Generated with Claude Code

Person discovery renders the page, walks the pagination, opens each entry
and parses a name, a title and a profile link out of it. The campaign
runner then read `prospects` and `errors` off that result and dropped
`people` on the floor.

So every person a campaign found was discarded at the last step, after
being paid for. The contacts table held eighty rows and not one name,
which looked like person extraction being broken and was really the one
caller that mattered ignoring its output.

The one-shot finder did it correctly, which is exactly why nobody
noticed: the path used to test the feature worked, and the path used to
run it did not.

Both now go through one recorder rather than two copies, since two copies
is what let them drift. People found by a campaign also carry that
campaign's niche, matching how contacts found through the prospect route
are already labelled.

Verified against the directory that exposed it: eleven people extracted
and eleven recorded, with titles — "Chief Technology Officer",
"Co-Founder and CTO", "Fractional CTO" — where before there were none.
The names lost earlier cannot be backfilled, since they were never
written down anywhere; re-running the campaign recovers them.

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 5125bc0 into master Jul 28, 2026
8 checks passed
@ralyodio
ralyodio deleted the fix/person-mode-contacts branch July 28, 2026 13:50
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