listen · signals
Discards and negatives
Every no, with its reason.
In Signals
Most of what a detector catches isn't a signal. Throwing those matches away loses half of every training set: a judge can only learn what "no" looks like from real noes. So every discard is kept, with the reason, the check that made it, and the text as it was.
match → checks and judge → discard · kept with its reason → audit · fix the rule or the judge
rule 1 — Every no keeps its reason · One reason per discard, from a fixed list, plus the check that produced it. "Filtered" is not a reason.
rule 2 — Freeze the text · Posts get edited and deleted. The discard keeps the text the check actually saw.
rule 3 — Audit a sample · A small random share of discards goes to the founder. Without it, the judge only ever grades itself.
what it is
Negative examples, kept on purpose.
In machine-learning terms discards are negative examples, and the ones that nearly passed are hard negatives: the most useful kind. In ops terms they're the reject log of a filter. Both uses need the same thing: the reason and the input, kept.
Discard — rejected match · A match that failed a check or the judge.
Reason code — rejection reason · One of ten fixed reasons, so they can be counted and compared.
Hard negative — near miss · A discard the judge was unsure about, or that shared most of the vocabulary. The best training data.
Audit sample — random review · A small random share of discards shown to the founder to catch wrong noes.
Exclusion — negative rule · A detector-level rule that stops a whole class of noise before the judge.
Class balance — positives vs negatives · Signals are rare. Training needs the noes in proportion, or sampled with weights.
how it works
Where a discard is made, and what happens to it.
Cheap rule checks run first; the judge sees what's left. Either way, the first failing check writes the discard.
- Match — rule fired
- Rule checks — fresh · excluded · lists · dupes
- Judge — 4 questions
- First failure — writes the discard
- Freeze inputs — text · author · versions
- Weekly count — reasons per detector
- Audit sample — founder grades
- Fix — exclusion or judge
Run the rule checks
Freshness, ICP not-if lines, competitor and customer lists, duplicate person. These are cheap and catch most noise.
Judge what's left
The judge's no carries one of its reason codes.
Write the discard at the first failure
With the reason, the check that made it (rule or judge), the detector and its version, and the frozen text.
Count reasons per detector each week
A detector that discards most of its catches for one reason needs an exclusion, not a smarter judge.
Audit a random sample
Show the founder a few discards a week. A wrong no becomes a label and a regression case.
Fix at the cheapest layer
Add an exclusion to the detector when one reason dominates; change the judge only for the cases rules can't see.
the object
The discard record.
One per rejected match. The founder's audit fields fill in only for the sampled ones.
Written once by the system; audited later for a sample.
| Field | Type | What it holds |
|---|---|---|
discard_id | uuid | |
match_id · raw_item_id | uuid | What was rejected |
detector_id · detector_version | text · int | Which rule caught it |
reason | enum | stale, excluded, competitor, customer, duplicate, not the trigger, vendor, no person, answered, out of scope |
stage | enum | rule or judge |
judgment_id | uuid | null | When the judge made it |
text_frozen · author_snapshot | text · json | Exactly what was checked |
near_miss | bool | Judge was unsure, or rule score was high: a hard negative |
audit_sampled · audit_label · audited_at | bool · enum · timestamp | Founder's grade on the sample: correct or wrong |
created_at | timestamp |
one discard, as data
{
"discard_id": "dsc_0b7",
"match_id": "mch_9a9",
"raw_item_id": "itm_reddit_t3_def456",
"detector_id": "det.pain.reddit",
"detector_version": 3,
"reason": "vendor",
"stage": "judge",
"judgment_id": "jdg_51c9",
"text_frozen": "what do you all use for release notes? (asking because I made one)",
"author_snapshot": {
"account_age_days": 40,
"bio": "building a changelog tool"
},
"near_miss": true,
"audit_sampled": true,
"audit_label": "correct",
"audited_at": "2026-10-18T14:02:00Z",
"created_at": "2026-10-17T03:15:10Z"
}the ten reasons
Each reason, where it's caught, and what it fixes.
The same ten reasons as the Signal catalog, with the layer that catches each. Rule checks are free; judge checks cost a model call.
| Caught by | Reason | What it means | Example | What it trains or fixes | Cost |
|---|---|---|---|---|---|
| rule | stale | Posted outside the trigger's freshness window. | a "any recommendations?" thread from three weeks ago | the freshness window per trigger; detection lag | cheap |
| rule | excluded | Hits one of the ICP's not-if lines. | an agency asking on behalf of a client | which ICP lines throw out real buyers | cheap |
| rule | competitor | A competitor, its staff or its affiliates. | an employee asking what people think of their new pricing | the competitor list | cheap |
| rule | customer | Already a customer, or already in a campaign. | a current user asking a support question in public | route to support or engage instead | cheap |
| rule | duplicate | The same person and trigger inside the window. | the same question cross-posted to three subreddits | person merging: one signal, three sources | cheap |
| judge | not the trigger | Shares the words, not the moment. | "I built a changelog tool, roast my landing page" | the judge's first question; negative phrases for the rule | model |
| judge | vendor | Selling, fishing for clicks, or promoting their own tool. | "what do you use for X? (asking because I built one)" | detector exclusions | model |
| judge | no person | A brand account, bot, news site or deleted user. | a newsletter account resharing a listicle | author-snapshot features | model |
| judge | answered | Already solved in the thread. | "went with X, thanks all" | thread state as a judge input | model |
| judge | out of scope | Wrong language, region or platform for the product. | asks for a tool that only works with an unsupported stack | brief fields for supported stack and region | model |
failure modes
How negatives go wrong.
Self-grading — Training the judge on discards the judge itself made teaches it its own bias. · catch itUse founder-audited discards and rule-made discards for training; weight judge-made ones low.
Too coarse — "Filtered" or "irrelevant" as the reason. · catch itA fixed list of ten; add a reason only when a new one keeps recurring.
Deleted posts — The text is gone when someone reviews the discard. · catch itFreeze the text at check time.
Class imbalance — Ninety-five noes to five yeses make any classifier say no. · catch itSample or weight the noes; measure precision and recall, not accuracy.
Nobody audits — The sample is shown and ignored. · catch itThree a day, one tap each, inside the daily list.
data to capture
What discards record.
The same four lines every stage uses, under the guide's eight data rules: log the decision, version what decided, freeze the inputs, keep the negatives, log the odds, every human touch is a label, one chain of ids, keep the raw.
record
- every discard with reason, stage, detector version and frozen text
- near-miss flag for hard negatives
- the audit sample and the founder's grade
label
- audited correct or wrong
- a discarded person who later replied or paid: a wrong no, found the hard way
trains
- the judge's negatives
- detector exclusions suggested from dominant reasons
- recall estimates per detector from the audit
test
- a new exclusion · offline replay on past discards and signals, check no real signals are lost
- audit sample size · how many a day before the founder stops