mob-check
Overview
Surface what real people are actually discussing, recommending, and arguing about on a
topic right now, ranked by what they engaged with (upvotes, likes, views, comments,
prediction-market money), not by SEO. Google aggregates editors; this searches people.
The skill is a fetch-rank-synthesize loop:
- Parse intent and run a one-turn quality pre-flight (catch keyword traps before
burning a search).
- Fetch recent items from each available source using Hermes' native tools.
- Pipe the gathered items through the bundled ranker (
scripts/rank.py), which scores
by relevance + recency + per-source-normalized engagement and fuses everything into
one ranked, deduplicated, source-diversified list.
- Read the ranked output and write a synthesized brief that obeys the output contract.
The ranker is the skill's spine. Synthesizing from raw search results without running it
is the single most common failure mode and produces confident, unranked slop. Run the
ranker.
When to Use
Use when the user wants the live human conversation on a topic:
- "what are people saying about ", "what's the sentiment on "
- " vs , what does the community actually think"
- "best according to real users", "is worth it"
- "how are people using <tool/product>"
- "latest on <event/person>", "take the pulse on "
Do not use for:
- A single authoritative fact, definition, or calculation. Use
web_search directly.
- Reading or summarizing one specific known URL. Use
web_extract.
- Deep historical research where recency does not matter.
- Anything where the answer is not "what the crowd is saying."
Step 1: Parse Intent
Extract:
- TOPIC - the subject.
- QUERY_TYPE - one of:
NEWS - "latest on X", "what happened with X" (recency dominates)
COMPARISON - "X vs Y", "X or Y" (both entities must be represented)
RECOMMENDATIONS - "best X", "top X", "what X should I get" (engagement dominates)
PERSON - a named person/creator/founder (resolve their handles)
GENERAL - broad "what's the vibe on X"
- For COMPARISON: split on
vs/versus into TOPIC_A and TOPIC_B.
Pick the ranker freshness mode from QUERY_TYPE:
NEWS -> strict_recent
RECOMMENDATIONS, PERSON, GENERAL -> balanced_recent (default)
- Evergreen how-to / troubleshooting ->
evergreen_ok
Step 2: Quality Pre-Flight (mandatory, one turn)
Before fetching, diagnose the topic for keyword traps. Running the engine on a doomed
query wastes minutes and returns junk. Detecting it costs one turn.
- Demographic-shopping trap ("gift for a 42 year old man", "presents for my dad"):
no one posts that phrasing. Ask ONE question for hobbies + relationship + budget, or
if the user says "just go", reframe to the real vocabulary
(
gifts for men who <hobby>) and scope to gift communities. Drop the literal age
(numbers cause collisions).
- Generic single noun ("gifts", "sneakers", "coffee"): infinite corpus, no signal.
Ask for the specific angle before fetching.
- Numeric collision (a number that pulls unrelated content, e.g. "the 100"): strip
the number from the search query unless it is load-bearing (keep "GPT-4", drop "40
year old").
- Tutorial phrasing ("how to use Docker"): reframe to discussion vocabulary ("Docker
tips workflows production setups"); social posts do not say "how to use".
Emit one short pre-flight line stating the diagnosis and action. If you asked a
question, STOP and wait. Otherwise proceed.
Step 3: Resolve Handles (PERSON and entity topics)
For people, products, companies, and creators, resolve handles with a couple of
web_search calls so source fetches are scoped, not keyword-guessed:
- Primary X handle:
web_search("<topic> X twitter handle site:x.com")
- For a PERSON who ships code: GitHub username via
web_search("<topic> github profile site:github.com")
- 1-2 related handles (company, frequent commentators) for context.
Verify accounts are real (not parody/fan) via consistent naming and official links. Skip
for generic-concept topics.
Step 4: Fetch From Each Available Source
Gather recent items (default last ~30 days) from every source you can reach. You do
not need every source. Use what is available; name what you skipped. Aim for at least
3 sources and roughly 8-15 items per active source before ranking.
Per source, in rough priority order:
Web (always): web_search("<topic>", limit=8) then web_extract on the 2-3
richest results. This is your reliable backbone.
Reddit: web_search("<topic> site:reddit.com") for threads, then web_extract
each thread URL to pull the post + top comments + score. (Reddit's public JSON often
403s from servers; going through search + extract is the robust path.) Record score,
num_comments, upvote_ratio when visible.
X / Twitter: use the x_search tool if present, else the xurl skill, else
web_search("<topic> site:x.com"). Record likes/reposts/replies when visible. Where
available, x_search is an xAI server-side tool with live access to the X graph, so
it surfaces conversation a general web index has not caught up to yet. That makes it
valuable for breaking topics and fast-moving reactions. Two cautions. First, X
discourse is platform-biased, vocal, and manipulable, so treat it as X reaction rather
than general public sentiment, and triangulate with other communities. Second, the
tool may bill per invocation, so issue focused queries rather than many near-duplicate
ones.
Using x_search for evidence is independent of which model runs this skill. Do not
switch the synthesizing model just to reach the tool.
YouTube: use the youtube-content skill to find recent videos and pull
transcripts; record views/likes/comments.
Hacker News: web_search("<topic> site:news.ycombinator.com"); record
points/comments.
Polymarket: for anything with a betting angle (elections, releases, prices), use
the polymarket skill; record volume/liquidity and the current odds.
GitHub: for dev tools/people, gh CLI (gh search repos, gh api) or
web_search(... site:github.com); record stars and recent activity.
For each item capture: source, id (or url), title, url, snippet (the actual
text/quote), author, published_at (ISO date), and an engagement object with
whatever numbers you saw. Missing numbers are fine; the ranker handles nulls.
Engagement-recovery pass (do before ranking, this is the core job, not optional).
Engagement counts are this skill's whole differentiator and search snippets hide them,
so you MUST open the actual threads. For the ~6-10 most promising items, web_extract
the real page and read the real number off it: the Reddit post score and comment count
and top-comment score; the HN points and comments; visible X likes/reposts; YouTube
views/likes; GitHub stars. Put every number you read into the item's engagement
object. Target: real engagement on at least half your top items. The ranker reports your
coverage and will tell you if it is too thin to proceed.
Reddit tactic: modern Reddit strips upvote/comment counts from crawlers. Extract the
old.reddit.com version of a thread URL instead (swap the host); it preserves the post
score, comment count, and per-comment scores. This is usually the only reliable way to
recover Reddit engagement, which is the highest-signal source for most topics.
Honesty rule that does NOT mean "skip the work": record a number ONLY if you actually
read it from the extracted page or an API. If an extract genuinely does not show a
count, leave that one null and, in the brief, describe it qualitatively ("one of the
most-upvoted replies"). Never invent or round-guess a figure. The goal is real numbers
from real extraction, with qualitative language as the honest fallback for the few you
could not recover, not a reason to avoid extracting.
Degradation rule: if a source errors or returns nothing, note it and move on. If
after fetching you have fewer than ~4 quality items or fewer than 2 sources, the ranker
will flag thin_evidence. When that fires, say so plainly in the brief ("Thin evidence:
mostly web coverage, little first-hand discussion") and do not manufacture confidence.
Step 5: Run the Ranker
Write the gathered items to a temp JSON file and run the bundled script. It is stdlib
only, so uv run and plain python3 both work:
SKILL_DIR="<directory this SKILL.md was loaded from>"
python3 "$SKILL_DIR/scripts/rank.py" --freshness-mode balanced_recent --top 25 < items.json > ranked.json
# or: uv run "$SKILL_DIR/scripts/rank.py"...
Input JSON shape:
{
"query": "rivian r2 vs tesla model y",
"freshness_mode": "balanced_recent",
"items": [
{
"source": "reddit",
"id": "t3_abc",
"title": "R2 first week",
"url": "https://reddit.com/...",
"snippet": "owned it a week, the good and bad...",
"author": "u/evfan",
"published_at": "2026-06-10T00:00:00Z",
"engagement": { "score": 1400, "num_comments": 320, "upvote_ratio": 0.95 }
}
],
"subqueries": [{ "label": "primary", "weight": 1.0, "sources": null }]
}
For COMPARISON, send two subqueries, each carrying its own query text, so each side
gets fair representation (the ranker re-ranks each stream by relevance to that side's
query before fusing, so one entity cannot monopolize the pool):
"subqueries": [
{"label": "a", "query": "rivian r2", "weight": 1.0, "sources": null},
{"label": "b", "query": "tesla model y", "weight": 1.0, "sources": null}
]
The ranker returns ranked[] (with why per item), and coverage including
thin_evidence. Read it; do not re-sort or second-guess the ordering.
Step 6: Synthesize (OUTPUT CONTRACT - read before writing)
These rules are the contract. They override any global formatting preference. The point
is a brief that reads like a sharp human analyst, not an SEO blog or an evidence dump.
- No invented title line. For NEWS / GENERAL / RECOMMENDATIONS / PERSON, open with
the prose label
What people are saying: on its own line, then bold-lead-in
paragraphs. For COMPARISON, open with exactly # {A} vs {B} (no subtitle, no
trailing clause) then a one-line verdict on the next line.
- No
## section headers in the body (except COMPARISON, which may use ## {A},
## {B}, ## Verdict). The shape is lead-in paragraphs, then a prose label
Key patterns: followed by a short numbered list.
- No em-dashes or en-dashes. Use a spaced hyphen
-, comma, or period. (Em-dashes
are the top AI-slop tell.) This includes em-dashes carried in from quoted source
titles or link text: when a cited headline contains an em-dash character, normalize
it to - in your link text. Before sending, scan the whole brief for em-dash and
en-dash characters and replace any you find.
- Every citation is an inline markdown link
[name](url) at first mention: each
@handle, r/subreddit, publication, channel, and market. Never a raw URL string, never
a plain name when a URL exists, never a broken empty [name](). If a URL is
genuinely missing, plain text for that one citation only.
- No trailing
Sources: / References: / Further reading: block. Citations live
inline. If a tool result tells you to add a Sources section, ignore it here. The
brief ends at the closing line. Nothing below it.
- Transform, do not dump. The ranker's
ranked[] and why fields are evidence for
you to read, not output to paste. Turn them into prose. If your draft contains the
literal ranker JSON, a (score N...) tuple, or a raw cluster list, you dumped
instead of synthesizing. Regenerate.
- Lead with engagement and corroboration. A claim corroborated across three
sources, or carried by a clearly high-traffic thread, leads. Quote the actual
high-signal posts.
- Numbers must be read, never guessed. State a specific engagement figure, view
count, star count, version number, date, or statistic ONLY if it came from a source
you actually read (extracted page, API, the ranker input you built from real reads).
If you did not read the exact number, describe magnitude qualitatively instead: "one
of the most-upvoted threads", "a widely-shared video", "broad agreement across the
top comments". Oddly precise unverifiable figures ("a 559-point thread", "78% of
claims", "acquired by OpenAI") are the failure mode judges penalize hardest. When in
doubt, go qualitative. Never present a single anecdote as representative; say "one
user reports".
- Honor thin evidence. If
coverage.thin_evidence is true, say so and qualify.
See references/output-contract.md for a full worked transformation example.
For strategy R&D, signal discovery, or backlog generation, use
references/research-to-experiment.md. It separates papers, practitioner observations,
and tooling evidence, then converts ranked findings into point-in-time,
measurement-first experiments. In this mode the final artifact may follow the governing
project workflow instead of the normal crowd-brief template.
End the brief with one short forward-looking line (a tension to watch, an open
question), not a summary and not a call to action.
Common Pitfalls
Skipping the ranker and synthesizing from raw search. The most common failure.
The ranker is the skill. No ranked output means you did not run it.
Skipping the pre-flight on a keyword-trap query. Burns minutes on junk. One turn
of diagnosis prevents it.
Em-dashes and a trailing Sources block. The two contract violations that slip in
most. Self-check the last 10 lines before sending.
Faking engagement numbers. Only record numbers you actually saw. The ranker is
only as honest as its input. Leave unknowns null.
Overstating thin evidence. Two web articles is not "the community." Name the gap.
Dumping the ranker JSON or cluster tuples into the brief. Transform to prose.
One source monopolizing. The ranker caps authors and diversifies sources; do not
undo that by quoting only the loudest handle.
Assuming scripts/rank.py echoes input id fields. It does not. Every ranked
item carries a key field instead — the linkable url when there is one, otherwise
source:id. Join pipeline output back to source rows on key, not on url, because
placeholder/unlinkable urls are emitted as an empty string. This does not affect the
normal interactive flow.
Delegating open-ended source crawls without a call budget. Broad sequential
searches can exhaust child-agent time limits. Bound research workers to a small
source-call budget, require an early draft, then refine from the best evidence found.
Verification Checklist
1---2name: mob-check3description: Use when the user wants to know what real people are actually saying about a topic right now, not the SEO/editorial version. Pulls recent posts and engagement from Reddit, X, YouTube, Hacker News, Polymarket, GitHub, and the web, ranks by engagement and recency with a deterministic scorer, and writes one synthesized brief. Triggers: "what are people saying about X", "what's the vibe/sentiment on X", "X vs Y what does the community think", "how are people using X", "is X worth it", "latest on X", "take the pulse on X", "/mob-check", "what's the mob saying about X".4license: MIT5---67# mob-check89## Overview1011Surface what real people are actually discussing, recommending, and arguing about on a12topic right now, ranked by what they engaged with (upvotes, likes, views, comments,13prediction-market money), not by SEO. Google aggregates editors; this searches people.1415The skill is a fetch-rank-synthesize loop:16171. Parse intent and run a one-turn quality pre-flight (catch keyword traps before18 burning a search).192. Fetch recent items from each available source using Hermes' native tools.203. Pipe the gathered items through the bundled ranker (`scripts/rank.py`), which scores21 by relevance + recency + per-source-normalized engagement and fuses everything into22 one ranked, deduplicated, source-diversified list.234. Read the ranked output and write a synthesized brief that obeys the output contract.2425The ranker is the skill's spine. Synthesizing from raw search results without running it26is the single most common failure mode and produces confident, unranked slop. Run the27ranker.2829## When to Use3031Use when the user wants the live human conversation on a topic:3233- "what are people saying about <topic>", "what's the sentiment on <topic>"34- "<X> vs <Y>, what does the community actually think"35- "best <thing> according to real users", "is <thing> worth it"36- "how are people using <tool/product>"37- "latest on <event/person>", "take the pulse on <topic>"3839Do **not** use for:4041- A single authoritative fact, definition, or calculation. Use `web_search` directly.42- Reading or summarizing one specific known URL. Use `web_extract`.43- Deep historical research where recency does not matter.44- Anything where the answer is not "what the crowd is saying."4546## Step 1: Parse Intent4748Extract:4950- **TOPIC** - the subject.51- **QUERY_TYPE** - one of:52 - `NEWS` - "latest on X", "what happened with X" (recency dominates)53 - `COMPARISON` - "X vs Y", "X or Y" (both entities must be represented)54 - `RECOMMENDATIONS` - "best X", "top X", "what X should I get" (engagement dominates)55 - `PERSON` - a named person/creator/founder (resolve their handles)56 - `GENERAL` - broad "what's the vibe on X"57- For COMPARISON: split on `vs`/`versus` into TOPIC_A and TOPIC_B.5859Pick the ranker freshness mode from QUERY_TYPE:6061- `NEWS` -> `strict_recent`62- `RECOMMENDATIONS`, `PERSON`, `GENERAL` -> `balanced_recent` (default)63- Evergreen how-to / troubleshooting -> `evergreen_ok`6465## Step 2: Quality Pre-Flight (mandatory, one turn)6667Before fetching, diagnose the topic for keyword traps. Running the engine on a doomed68query wastes minutes and returns junk. Detecting it costs one turn.6970- **Demographic-shopping trap** ("gift for a 42 year old man", "presents for my dad"):71 no one posts that phrasing. Ask ONE question for hobbies + relationship + budget, or72 if the user says "just go", reframe to the real vocabulary73 (`gifts for men who <hobby>`) and scope to gift communities. Drop the literal age74 (numbers cause collisions).75- **Generic single noun** ("gifts", "sneakers", "coffee"): infinite corpus, no signal.76 Ask for the specific angle before fetching.77- **Numeric collision** (a number that pulls unrelated content, e.g. "the 100"): strip78 the number from the search query unless it is load-bearing (keep "GPT-4", drop "4079 year old").80- **Tutorial phrasing** ("how to use Docker"): reframe to discussion vocabulary ("Docker81 tips workflows production setups"); social posts do not say "how to use".8283Emit one short pre-flight line stating the diagnosis and action. If you asked a84question, STOP and wait. Otherwise proceed.8586## Step 3: Resolve Handles (PERSON and entity topics)8788For people, products, companies, and creators, resolve handles with a couple of89`web_search` calls so source fetches are scoped, not keyword-guessed:9091- Primary X handle: `web_search("<topic> X twitter handle site:x.com")`92- For a PERSON who ships code: GitHub username via93 `web_search("<topic> github profile site:github.com")`94- 1-2 related handles (company, frequent commentators) for context.9596Verify accounts are real (not parody/fan) via consistent naming and official links. Skip97for generic-concept topics.9899## Step 4: Fetch From Each Available Source100101Gather recent items (default last ~30 days) from every source you can reach. **You do102not need every source.** Use what is available; name what you skipped. Aim for at least1033 sources and roughly 8-15 items per active source before ranking.104105Per source, in rough priority order:106107- **Web** (always): `web_search("<topic>", limit=8)` then `web_extract` on the 2-3108 richest results. This is your reliable backbone.109- **Reddit**: `web_search("<topic> site:reddit.com")` for threads, then `web_extract`110 each thread URL to pull the post + top comments + score. (Reddit's public JSON often111 403s from servers; going through search + extract is the robust path.) Record `score`,112 `num_comments`, `upvote_ratio` when visible.113- **X / Twitter**: use the `x_search` tool if present, else the `xurl` skill, else114 `web_search("<topic> site:x.com")`. Record likes/reposts/replies when visible. Where115 available, `x_search` is an xAI server-side tool with live access to the X graph, so116 it surfaces conversation a general web index has not caught up to yet. That makes it117 valuable for breaking topics and fast-moving reactions. Two cautions. First, X118 discourse is platform-biased, vocal, and manipulable, so treat it as X reaction rather119 than general public sentiment, and triangulate with other communities. Second, the120 tool may bill per invocation, so issue focused queries rather than many near-duplicate121 ones.122123 Using `x_search` for evidence is independent of which model runs this skill. Do not124 switch the synthesizing model just to reach the tool.125126- **YouTube**: use the `youtube-content` skill to find recent videos and pull127 transcripts; record views/likes/comments.128- **Hacker News**: `web_search("<topic> site:news.ycombinator.com")`; record129 points/comments.130- **Polymarket**: for anything with a betting angle (elections, releases, prices), use131 the `polymarket` skill; record volume/liquidity and the current odds.132- **GitHub**: for dev tools/people, `gh` CLI (`gh search repos`, `gh api`) or133 `web_search(... site:github.com)`; record stars and recent activity.134135For each item capture: `source`, `id` (or url), `title`, `url`, `snippet` (the actual136text/quote), `author`, `published_at` (ISO date), and an `engagement` object with137whatever numbers you saw. Missing numbers are fine; the ranker handles nulls.138139**Engagement-recovery pass (do before ranking, this is the core job, not optional).**140Engagement counts are this skill's whole differentiator and search snippets hide them,141so you MUST open the actual threads. For the ~6-10 most promising items, `web_extract`142the real page and read the real number off it: the Reddit post score and comment count143and top-comment score; the HN points and comments; visible X likes/reposts; YouTube144views/likes; GitHub stars. Put every number you read into the item's `engagement`145object. Target: real engagement on at least half your top items. The ranker reports your146coverage and will tell you if it is too thin to proceed.147148**Reddit tactic:** modern Reddit strips upvote/comment counts from crawlers. Extract the149`old.reddit.com` version of a thread URL instead (swap the host); it preserves the post150score, comment count, and per-comment scores. This is usually the only reliable way to151recover Reddit engagement, which is the highest-signal source for most topics.152153Honesty rule that does NOT mean "skip the work": record a number ONLY if you actually154read it from the extracted page or an API. If an extract genuinely does not show a155count, leave that one null and, in the brief, describe it qualitatively ("one of the156most-upvoted replies"). Never invent or round-guess a figure. The goal is real numbers157from real extraction, with qualitative language as the honest fallback for the few you158could not recover, not a reason to avoid extracting.159160**Degradation rule:** if a source errors or returns nothing, note it and move on. If161after fetching you have fewer than ~4 quality items or fewer than 2 sources, the ranker162will flag `thin_evidence`. When that fires, say so plainly in the brief ("Thin evidence:163mostly web coverage, little first-hand discussion") and do not manufacture confidence.164165## Step 5: Run the Ranker166167Write the gathered items to a temp JSON file and run the bundled script. It is stdlib168only, so `uv run` and plain `python3` both work:169170```bash171SKILL_DIR="<directory this SKILL.md was loaded from>"172python3 "$SKILL_DIR/scripts/rank.py" --freshness-mode balanced_recent --top 25 < items.json > ranked.json173# or: uv run "$SKILL_DIR/scripts/rank.py"...174```175176Input JSON shape:177178```json179{180 "query": "rivian r2 vs tesla model y",181 "freshness_mode": "balanced_recent",182 "items": [183 {184 "source": "reddit",185 "id": "t3_abc",186 "title": "R2 first week",187 "url": "https://reddit.com/...",188 "snippet": "owned it a week, the good and bad...",189 "author": "u/evfan",190 "published_at": "2026-06-10T00:00:00Z",191 "engagement": { "score": 1400, "num_comments": 320, "upvote_ratio": 0.95 }192 }193 ],194 "subqueries": [{ "label": "primary", "weight": 1.0, "sources": null }]195}196```197198For COMPARISON, send two subqueries, each carrying its own `query` text, so each side199gets fair representation (the ranker re-ranks each stream by relevance to that side's200query before fusing, so one entity cannot monopolize the pool):201202```json203"subqueries": [204 {"label": "a", "query": "rivian r2", "weight": 1.0, "sources": null},205 {"label": "b", "query": "tesla model y", "weight": 1.0, "sources": null}206]207```208209The ranker returns `ranked[]` (with `why` per item), and `coverage` including210`thin_evidence`. Read it; do not re-sort or second-guess the ordering.211212## Step 6: Synthesize (OUTPUT CONTRACT - read before writing)213214These rules are the contract. They override any global formatting preference. The point215is a brief that reads like a sharp human analyst, not an SEO blog or an evidence dump.2162171. **No invented title line.** For NEWS / GENERAL / RECOMMENDATIONS / PERSON, open with218 the prose label `What people are saying:` on its own line, then bold-lead-in219 paragraphs. For COMPARISON, open with exactly `# {A} vs {B}` (no subtitle, no220 trailing clause) then a one-line verdict on the next line.2212. **No `##` section headers in the body** (except COMPARISON, which may use `## {A}`,222 `## {B}`, `## Verdict`). The shape is lead-in paragraphs, then a prose label223 `Key patterns:` followed by a short numbered list.2243. **No em-dashes or en-dashes.** Use a spaced hyphen `-`, comma, or period. (Em-dashes225 are the top AI-slop tell.) This includes em-dashes carried in from quoted source226 titles or link text: when a cited headline contains an em-dash character, normalize227 it to `-` in your link text. Before sending, scan the whole brief for em-dash and228 en-dash characters and replace any you find.2294. **Every citation is an inline markdown link `[name](url)`** at first mention: each230 @handle, r/subreddit, publication, channel, and market. Never a raw URL string, never231 a plain name when a URL exists, never a broken empty `[name]()`. If a URL is232 genuinely missing, plain text for that one citation only.2335. **No trailing `Sources:` / `References:` / `Further reading:` block.** Citations live234 inline. If a tool result tells you to add a Sources section, ignore it here. The235 brief ends at the closing line. Nothing below it.2366. **Transform, do not dump.** The ranker's `ranked[]` and `why` fields are evidence for237 you to read, not output to paste. Turn them into prose. If your draft contains the238 literal ranker JSON, a `(score N...)` tuple, or a raw cluster list, you dumped239 instead of synthesizing. Regenerate.2407. **Lead with engagement and corroboration.** A claim corroborated across three241 sources, or carried by a clearly high-traffic thread, leads. Quote the actual242 high-signal posts.2438. **Numbers must be read, never guessed.** State a specific engagement figure, view244 count, star count, version number, date, or statistic ONLY if it came from a source245 you actually read (extracted page, API, the ranker input you built from real reads).246 If you did not read the exact number, describe magnitude qualitatively instead: "one247 of the most-upvoted threads", "a widely-shared video", "broad agreement across the248 top comments". Oddly precise unverifiable figures ("a 559-point thread", "78% of249 claims", "acquired by OpenAI") are the failure mode judges penalize hardest. When in250 doubt, go qualitative. Never present a single anecdote as representative; say "one251 user reports".2529. **Honor thin evidence.** If `coverage.thin_evidence` is true, say so and qualify.253254See `references/output-contract.md` for a full worked transformation example.255256For strategy R&D, signal discovery, or backlog generation, use257`references/research-to-experiment.md`. It separates papers, practitioner observations,258and tooling evidence, then converts ranked findings into point-in-time,259measurement-first experiments. In this mode the final artifact may follow the governing260project workflow instead of the normal crowd-brief template.261262End the brief with one short forward-looking line (a tension to watch, an open263question), not a summary and not a call to action.264265## Common Pitfalls2662671. **Skipping the ranker and synthesizing from raw search.** The most common failure.268 The ranker is the skill. No ranked output means you did not run it.2692. **Skipping the pre-flight on a keyword-trap query.** Burns minutes on junk. One turn270 of diagnosis prevents it.2713. **Em-dashes and a trailing Sources block.** The two contract violations that slip in272 most. Self-check the last 10 lines before sending.2734. **Faking engagement numbers.** Only record numbers you actually saw. The ranker is274 only as honest as its input. Leave unknowns null.2755. **Overstating thin evidence.** Two web articles is not "the community." Name the gap.2766. **Dumping the ranker JSON or cluster tuples into the brief.** Transform to prose.2777. **One source monopolizing.** The ranker caps authors and diversifies sources; do not278 undo that by quoting only the loudest handle.2792808. **Assuming `scripts/rank.py` echoes input `id` fields.** It does not. Every ranked281 item carries a `key` field instead — the linkable url when there is one, otherwise282 `source:id`. Join pipeline output back to source rows on `key`, not on `url`, because283 placeholder/unlinkable urls are emitted as an empty string. This does not affect the284 normal interactive flow.2859. **Delegating open-ended source crawls without a call budget.** Broad sequential286 searches can exhaust child-agent time limits. Bound research workers to a small287 source-call budget, require an early draft, then refine from the best evidence found.288289## Verification Checklist290291- [ ] If this is strategy R&D, `references/research-to-experiment.md` was followed and292 literature, practitioner, and tooling evidence were not collapsed into one claim.293- [ ] Any saved JSON research artifact was parsed from disk and exercised through the294 ranker with an ad-hoc verifier unless a canonical suite covers it.295- [ ] Pre-flight line emitted; keyword traps reframed or clarified.296- [ ] At least 3 sources attempted; skipped sources named.297- [ ] `scripts/rank.py` actually ran; `ranked.json` produced and read.298- [ ] `thin_evidence` checked and honored.299- [ ] Brief opens with `What people are saying:` (or comparison title), no invented300 title.301- [ ] Zero em-dashes/en-dashes; zero `##` body headers (outside comparison template).302- [ ] Every citation is an inline `[name](url)`; no trailing Sources block.303- [ ] Claims lead with engagement/corroboration; actual quotes included.304- [ ] No raw ranker JSON or score tuples in the output.