Skip to content

Read a company's site once, however many people it names - #63

Merged
ralyodio merged 1 commit into
mainfrom
fix/crawl-dedupe
Aug 19, 2026
Merged

Read a company's site once, however many people it names#63
ralyodio merged 1 commit into
mainfrom
fix/crawl-dedupe

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The actual runaway behind #61 and #62.

A crawl of accenture.com returns 57 people. Each becomes a refresh_research card. Each card queued another crawl of the same page, which returned the same 57.

Production before it was stopped:

226 × https://accenture.com     ← identical, all pending
 81 × https://toptal.com
 35 × https://trailofbits.com
 33 × https://bairesdev.com

Why #62 did not catch it

The per-person cooldown was the right mechanism on the wrong axis. It bounds how often one person is re-researched; the amplification is per site. Every one of those 57 people was brand new, so none had any research history to be cooled down against.

The samples said so plainly — cooldowns flat at zero while pending crawls climbed:

sample   pendCrawl  research  approvals  cooldowns
1        300        246       356        0
2        426        129       467        0
...
12       424        240       467        0

The fix

enqueue has supported a dedupe key since migration 0007, and the index behind it covers only pending and running jobs, so a key frees itself the moment the job finishes — it suppresses duplicates, never future work.

POST /prospects/by-url has passed one from the start. Neither approval path ever did. That is why one route could not queue a host twice while another queued it 226 times.

Both approval paths now pass the same key that route uses — crawl:<host>, without www. — because two paths deduping under different keys do not deduplicate against each other, which is the entire point. Keyed on host rather than URL so example.com and https://www.example.com/ are the one crawl they actually are.

The cards still all clear. Emptying the queue was never the problem.

Verification

  • bun test1369 pass, 0 fail across 90 files
  • typecheck and format:check clean
  • 2 new tests: eight people at one company produce one crawl; and the key frees itself once that crawl is done, so a later re-read still happens

Prod state while this lands

  • auto_approve_internal paused on wsp_04wz5p9… — the loop is stopped
  • 407 duplicate pending crawls purged (423 → 16)
  • Re-enable after this deploys

🤖 Generated with Claude Code

The runaway behind #61 and #62. A crawl of accenture.com returns 57
people; each becomes a `refresh_research` card; each card queued another
crawl of the same page, which returned the same 57. Production reached
226 pending crawls of that one URL and 81 of toptal.com before it was
stopped.

The per-person cooldown in #62 could not catch this, and the samples said
so plainly: cooldowns stayed at zero while pending crawls climbed 227 to
426. Every one of those 57 people was new, so none of them had any
research history to be cooled down against. The cooldown was the right
mechanism for the wrong axis — it bounds how often one person is
re-researched, and the amplification is per *site*.

`enqueue` has supported a dedupe key since 0007, and the index behind it
deliberately covers only pending and running jobs so a key frees itself
when the job finishes. `POST /prospects/by-url` has passed one from the
start. Neither approval path ever did, which is why one route could not
queue a host twice and another could queue it 226 times.

Both approval paths now pass the same key that route uses —
`crawl:<host>` without `www.` — because two paths deduping under
different keys do not deduplicate against each other, which is the whole
point. Keyed on the host rather than the URL, so `example.com` and
`https://www.example.com/` are the one crawl they actually are.

The cards still all clear; only the duplicate crawls collapse. Emptying
the queue was never the problem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 067ad95 into main Aug 19, 2026
4 checks passed
@ralyodio
ralyodio deleted the fix/crawl-dedupe branch August 19, 2026 17:07
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