WinnowLead-Qualifier Agent
What it does for you
Social media messages become a ranked list of real leads.
Turns messy inbound DMs into a ranked, deduped lead list with a readable score, a pipeline stage, a next step, and a casual reply draft, then exports to a CRM only after a preview.

Who it's for
Businesses whose ads bring in a flood of direct messages, most of them not buyers. It was built around a consultancy that helps people start home care agencies, and every reply is a draft the owner sends by hand.
Screenshots

In more detail
Winnow reads the DMs a home care startup consultancy gets from its ads, works out who is ready to buy, scores and tags each person, and drafts a short casual reply for each one, then exports the list to a CRM. Ads for "start your own home care agency" bring in a flood of DMs, and most aren't buyers: "how much" with nothing else, caregivers looking for jobs, people looking for care for a parent, spam. The real buyers get buried, and a slow reply loses the lead.
Honest limits
All names, phones, and emails are made up, and no real inbox or CRM was touched. The weights and the 60 and 30 point bars for Qualified and Nurture are my own guesses, not tuned on real outcomes. Replies are always drafts the owner sends by hand.
How I built it
Winnow is a featured build and one of the Claude Cowork projects. I told Cowork to one-shot all the Cowork projects and set the scope for this one: score and tag inbound DMs, dedupe people across platforms, casual texting replies with no em dashes, one follow-up question instead of a form, and a CRM push that waits for approval. A separate Cowork session built it and dropped it into the repo with the rest of the Cowork set.
Claude Cowork specced the signal model and weights, wrote 33 fictional DMs (messy on purpose: "price??", Spanish, a typo'd follow-up from a second account, spam), then built the core, tests, UI, a CLI, and a Cowork skill. One DOM-free module, qualifier.js, is shared by the page, the CLI, and 364 assertions. It didn't call the real Anthropic API or any connector during the build.
The core idea: the score is a plain function of signals and a rules object, and every factor returns its weight, its points, and a sentence of why. That one choice gives you the breakdown on each lead, the live sliders that rescore everyone, the CSV breakdown field, and a safe Claude merge. Claude only ever proposes signals, and local code does the scoring.
What I learned
- Two-letter state codes are mostly English words ("OK," "IN," "ME," "HI") or job titles ("MD," "PA"). A code only counts when it's uppercase and unambiguous or follows a place word, and claiming character spans stops "West Virginia" from also reading as Virginia.
- Jaro-Winkler alone is too generous for merging people. It matched "Kevin Brown" with "Kevin Browning," so fuzzy merges now also need a surname edit distance of 1 and no conflicting states.
- Lead ids come from the earliest message's platform and handle, not the email. Otherwise the id changes the day someone shares an email, and the CRM gets a duplicate row.
- Readiness keywords need a subject. Bare "licensed in" pushed a researcher into the existing-agency bucket until we required "we are" or "already" before it.
What carried forward
It pairs with Sieve (69): both are draft-only agents with a Cowork skill, and both keep a person in the loop before anything leaves the building. The "Claude proposes, plain tested code decides" pattern is the same one Ledgerly (67) uses for its category chips. The stable-id rule matters for anything that pushes to a CRM: 33 DMs merge into 24 people, and the tests shuffle the input and add newer messages to prove the ids hold.
If I took it further
I'd read DMs straight from the inbox through a connector on a schedule with a small memory per lead, and tune the weights from which leads actually booked.
#070 Built with Claude Cowork September 23, 2026 364 automated checks