Repository navigation
feat(leads): charge for lead generation, then sell it - #151
Merged
Merged
Conversation
LEAD_RUN_CREDITS was defined and never read. Every campaign tick and every manual search has been running on the house, and the expensive part is not the model — it is the search calls behind contact lookup. The one-shot finder was the wider hole: it budgeted up to a hundred paid lookups per click and charged nothing for any of them. So the meter goes in before the sales page, not after it. A tick is charged once, lazily, at the first stage that actually reaches for search or a model. Charging up front would bill 288 credits a day to leave an idle campaign switched on, and the cron cannot know what a tick will do until it looks — a prospect whose scan has not landed costs nothing to skip. A run that is charged and produces nothing gives the money back; some of it is genuinely spent by then, but billing for a run with no output is the worse trade. Running out pauses the campaign and says so in the run history, which is the only place a stalled campaign gets to explain itself. The manual finder is priced per hundred leads asked for, and the search budget it buys scales with it. Sizing that budget by the request instead would let a thousand-lead run buy a hundred lookups for the price of ten. The size picker goes up to a thousand to match — it was still offering 5/10/25 against an action that already accepted 1k, so a directory page reported a fraction of itself and looked broken. The pure pricing lives in lib/credits.ts beside the other prices rather than next to the deduction, so the form can show the cost before the click without pulling a database client into the browser bundle. Then the selling: a /lead-generation page, a card on the home grid, a footer link, and a section on pricing for what credits buy besides scans. Two things the page deliberately does not claim are open tracking and automatic reply detection, because neither is built. The regression guard reads the source. A unit test of the pricing function would have passed for the entire time the price was unreferenced — the bug was that nobody called it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The thing that was actually broken
LEAD_RUN_CREDITSwas defined and never read:One hit. The declaration. Every campaign tick and every manual search has been free, and the expensive part is not the model — it's the search calls behind contact lookup. The one-shot finder was the wider hole: it budgeted up to 100 paid lookups per click and charged nothing.
So the meter goes in before the sales page, not after it.
Charging a tick that doesn't know yet what it will do
Charged once, lazily, at the first stage that actually reaches for search or a model.
The manual finder is priced by size
Per hundred leads asked for, and the search budget scales with it:
Sizing the budget by the request instead is exactly what let a 1,000-lead run buy 100 lookups for the price of 10.
The size picker now goes to 1,000 to match — it was still offering 5/10/25 against an action that already accepted 1k, so a directory page reported a fraction of itself and looked broken.
Pure pricing moved to
lib/credits.tsbeside the other prices rather than next to the deduction, so the form can show the cost before the click without pulling a database client into the browser bundle.Then the selling
/lead-generation— the feature pageModuleCardgrew an optionalhref; cards without one stay non-clickable rather than eight of nine looking live and doing nothing)/pricing, which until now priced only scansTwo claims the page deliberately doesn't make: open tracking and automatic reply detection. Neither is built.
The guard reads the source
A unit test of
manualRunPricewould have passed for the entire time the price was unreferenced. The bug was that nobody called it — so four tests assert the call sites exist.Checks
tsc --noEmitclean · 966 tests pass (17 new) · build compiles,/lead-generationrenderscredits_spentonoutreach_campaign_runsapplied to prodHeads-up: two active campaigns on the account start drawing down a 1,760-credit balance once this lands.
🤖 Generated with Claude Code