feat(leads): alert on intent, and match on what you sell - #160
Merged
Merged
Conversation
Adds prd/0001, a read of Leadmatically against what we shipped this week. Their scoring is comparable to ours and in two respects ours is stricter — we refuse people who wrote "no vendors", and we refuse drafts that state anything the campaign never claimed. They beat us on everything that happens after a signal is found, which is where the value of finding it goes. This implements the two P0s. We were finding requests and telling nobody. A conversation worth answering has a useful life measured in hours, and a signal that sits in a table until somebody opens the Leads page is indistinguishable from one that was never found. Alerts are batched per campaign per sweep, because eleven emails for eleven conversations is the fastest way to make the feature something people filter to a folder. The count and best score go in the subject so it is triageable from a phone. Dedupe is a column on the row rather than a time window, and the row is marked only after the send succeeds — the other order loses a batch to one SMTP hiccup. And we matched on keywords alone, which drops the requests most worth having. "Our checkout keeps timing out whenever we run a promo" is exactly the buyer a load-testing campaign wants and contains none of its keywords, because people with a problem describe the problem rather than the product category that solves it. A campaign can now say what it sells in prose, and a keyword miss becomes a question rather than a verdict. That path is deliberately narrow. It only judges signals the cheap path already scored above the bar, it is capped per sweep so an index that returns three hundred results cannot become three hundred model calls, it cannot overturn a stated no or rescue somebody advertising their own services, and it has to quote the words that decided it — a keyword match is wrong in obvious ways, a model judging relevance is wrong in ways nobody sees unless it says why. Two things in their product are deliberately not copied. Facebook and Instagram monitoring, because neither carries meaningful public buying intent and neither is readable without access we do not have, so listing them would be coverage we cannot deliver. And per-keyword subscription tiers, because bolting a second quota beside credits would make the cost of a run unpredictable, which is the one thing this week's billing work was for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This was referenced Jul 30, 2026
ralyodio
added a commit
that referenced
this pull request
Jul 30, 2026
A backup of a local environment file was committed to master in 925da5e (#160, 2026-07-28) and has been tracked ever since. It carries live credentials, so gitleaks has been failing on every PR since that merge. The old ignore list named each env variant explicitly — .env, .env.local, .env.development and so on — so a .env.bak-<date> matched none of them. Replaced with `.env*` plus a negation for the checked-in .env.example, which fails closed instead of open. This stops the file being carried forward and closes the gitignore gap. It does NOT clear the gitleaks check: that job runs `gitleaks detect --source .`, which scans history, so the findings still resolve to 925da5e regardless of what HEAD looks like. See the PR for the options. Nor does it undo the exposure — the values are in the repository's history and need rotating on their own schedule. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
added a commit
that referenced
this pull request
Jul 30, 2026
`gitleaks detect` scans history, so the 21 findings from the environment backup committed in cfb23d2 (#160) keep failing the scan no matter what HEAD contains — untracking the file did nothing for them. The alternative is rewriting master to drop the blob, which forces all 8 collaborators to re-clone and still leaves the objects on GitHub's servers. Appended to the existing ignore list rather than replacing it; the two false-positive pins already there (the docs placeholder and the tracker test fixture) are untouched. These 21 differ from those two in kind: the values were real, not false positives. Pinning them is only honest after rotation, since rotation is what makes the recorded values dead. The file says so, at length, where the next person to read it will see it. Verified against a pristine clone of master with gitleaks 8.21.2 — the same version CI installs: 21 findings before, "no leaks found" after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
added a commit
that referenced
this pull request
Jul 30, 2026
A backup of a local environment file was committed to master in cfb23d2 (#160, 2026-07-28) and has been tracked ever since. It carries live credentials, so gitleaks has been failing on every PR since that merge. The old ignore list named each env variant explicitly — .env, .env.local, .env.development and so on — so a .env.bak-<date> matched none of them. Replaced with `.env*` plus a negation for the checked-in .env.example, which fails closed instead of open. This stops the file being carried forward and closes the gitignore gap. It does NOT clear the gitleaks check: that job runs `gitleaks detect --source .`, which scans history, so the findings still resolve to cfb23d2 regardless of what HEAD looks like. See the PR for the options. Nor does it undo the exposure — the values are in the repository's history and need rotating on their own schedule. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Adds
prd/0001-intent-lead-monitoring.md— a read of Leadmatically ($19–$1,499/mo) against what we shipped this week — and implements its two P0s.What they have that we don't
Their scoring is comparable to ours, and in two respects ours is stricter: we refuse people who wrote "no vendors", and we refuse drafts stating anything the campaign never claimed. They beat us on everything that happens after a signal is found — which is where the value of finding it goes.
R1 — we were finding requests and telling nobody
A conversation worth answering has a useful life measured in hours. A signal sitting in a table until someone opens the Leads page is indistinguishable from one never found.
found_atagainst a last-alerted timestamp double-sends anything that arrives mid-compose.R2 — keyword matching drops the best requests
Exactly the buyer a load-testing campaign wants. Contains none of its keywords — because people with a problem describe the problem, not the product category that solves it.
A campaign can now describe what it sells in prose, and a keyword miss becomes a question rather than a verdict. The path is deliberately narrow:
Deliberately not copied
Checks
tsc --noEmitclean · 1,100 tests pass (13 new) · build compilesRemaining in the PRD: R3 outcome tracking, R4 per-source analytics, R5 reply prompts, R6 Slack/Telegram, R7 keyword buckets.
🤖 Generated with Claude Code