Testimonial Miner

SkillDev tools

Mines the praise you already earned into usable social proof. Trigger on "find my testimonials", "testimonial bank", "do I have any good client quotes", "social proof for the launch", "customer quotes", "can I use this review", "permission to quote a client", "case study quotes", "who has said nice things about us". Sweeps captured DMs, comments, reviews and meeting transcripts for praise, wins and results moments, then banks each as a verbatim quote with attribution, date said, a receipt and a permission tier: public and reusable, private and needs permission, or confidential and do not use. Separates results quotes into their own sub-bank with FTC typicality flags, drafts permission requests held for approval, and reports which client relationships have no captured praise at all. Internal artifact. Nothing is published and nothing is sent.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Testimonial Miner skill

What this skill tells your AI

The instructions your AI receives, as published by legioncodeinc/vibe-coding-tools in src/quarantine/skills/keep/testimonial-miner/SKILL.md and read by ahel’s review.

Most people who run a business are sitting on more praise than they can remember. It arrived as a DM at 11pm, as a line on a call nobody wrote down, as a comment under a post six months ago. None of it is where anyone can find it, so the website says nothing and the launch page has a blank space where the proof should be.

This skill finds that material and turns it into a bank: verbatim quote, who said it, their role and company, the date said, the source and its receipt, and a permission tier.

The permission tier is the most important field in the skill. Not the quote, not the score. The tier, because it is the difference between social proof and a person discovering their private message on a stranger's landing page.

Capability gate

This skill requires the Littlebird MCP on a Power or Pro plan.

Before anything else:

  1. List the tools actually available in this session and use the real tool names. Do not assume a tool exists because it is named in references/littlebird-mcp-reference.md.
  2. If no Littlebird MCP tools are present, stop and tell the user the skill needs the Littlebird MCP connected. Do not attempt a partial run from memory or from other sources.
  3. If routine creation is part of the request, call LB_INTERNAL_GET_SUBSCRIPTION_STATUS first to confirm the plan supports another routine.

Read references/evidence-standards.md before writing any output. Every line in the bank is observed, inferred, external or unknown, and the kind is visible to the reader. Rule 10, reporting on people, and rule 4, the attribution guardrail, govern this skill harder than any other in the marketplace, because the output is other people's words published under their names.

Purpose

Convert praise the user already earned into an internal quote bank that is safe to draw on, and make the permission status of every quote unambiguous before anyone reaches for it.

Three problems it solves, in order of how much damage they do:

  1. The user publishes something they did not have the right to publish. Usually a warm private message, lifted because it was the best line anyone ever wrote about them.
  2. The user publishes a results claim they cannot support. A quote containing a specific outcome carries a legal requirement most people do not know exists. See below.
  3. The user has no idea what praise exists, so the good material sits in an archive and the website carries a placeholder.

Littlebird MCP calls used

ToolUsed for
search_user_context with filters.data_source: "messages"Gratitude, results and referral language in threads. The largest source of tier 2 material
search_user_context with filters.data_source: "snapshots"Public comments, reviews, recommendations and testimonial pages seen on screen. The largest source of tier 1 material and of false positives
search_user_context with filters.data_source: "summaries"The cheap compressed sweep, used to locate days worth a narrow re-query. Never a source of quotes, because a summary is a paraphrase
LB_INTERNAL_SEARCH_MEETINGSClients describing outcomes on calls. The best material and the most confidential
LB_INTERNAL_GET_MEETINGAttribution, from the summary's owner-tagged blocks, plus the attendee list from the linked calendar event
LB_INTERNAL_GET_MEETING_TRANSCRIPTExact wording only, for a line the summary already located and already attributed
LB_INTERNAL_GET_ROUTINE_REPORTSRead the monthly routine's own past reports before a deep run, so nothing is re-proposed and no decline is forgotten
LB_INTERNAL_CREATE_ROUTINE / LB_INTERNAL_UPDATE_ROUTINE / LB_INTERNAL_GET_ROUTINE_CONFIGOffer, create and maintain the monthly sweep
LB_INTERNAL_GET_SUBSCRIPTION_STATUSPlan check before creating a routine

There is no Littlebird tool that searches past Littlebird chat conversations. Where a skill would want one, search_user_context is the substitute.

Trigger

  • "find my testimonials", "build a testimonial bank", "what nice things have clients said"
  • "I need social proof for the launch", "quotes for the landing page", "case study quotes"
  • "can I use this review", "do I need permission to quote this client"
  • "who should I ask for a testimonial"
  • The monthly routine fires and the user comes to collect

Two modes:

ModeWhenWindowOutput
Monthly routineScheduled, unattendedLast 45 days plus carry-forward from its own past reportsA routine report naming new praise found and what changed. No files, no drafts, no approvals
Deep runUser asks, usually before a launch180 days, or 365 on an explicit widenThe full bank file, the results sub-bank, permission-request drafts held for approval, and the gap report

The routine observes. The deep run does the work. A routine cannot hold an approval gate open and cannot create routines (references/littlebird-mcp-reference.md).

Routine cadence

Monthly, on the 1st, 08:00 local. Monthly rather than weekly because praise arrives at a rate that makes a weekly report mostly empty, and an empty report trains the user to stop reading.

Plus on demand before a launch, a pitch, a site rebuild, or a proposal, which is when the bank actually gets spent.

Process

0. Roster and prior state

If testimonial-bank.md exists, read it first. It carries granted permissions, declines, revocations and prior tiers, and re-proposing a quote someone declined is the worst failure this skill can commit.

Then call LB_INTERNAL_GET_ROUTINE_REPORTS on the monthly routine if one exists, and read what it has already surfaced.

If no client roster exists, build one with AskUserQuestion. Do not infer it from meeting titles. Guessing turns prospects into clients and misses the client who only appears as a domain on a dashboard.

1. Sweep

Full retrieval brief in references/praise-discovery.md: the five passes, the ten register families, the 60-day blocking, and the deliberate gap sweep.

The governing design rule: praise is a register, not a topic, and the registers share almost no vocabulary. "Thank you so much for turning that around", "we went from four days to under an hour", and "I told Marcus he needs to call you" are all the same finding and no single query retrieves all three. Run many narrow parallel queries with deliberately varied phrasing. That is also what the server rewards (references/littlebird-mcp-reference.md).

Run the referral and expansion families even though they contain no praise words. A referral is invisible to gratitude-shaped queries, and an explicit recommendation is the form the evidence favors for infrequent purchases like professional services, an extension labeled as an inference where it appears (references/research/distilled-testimonial-practice.md, section 9).

2. Verify attribution before banking anything

Full procedure in references/attribution-verification.md: the five questions, the bona fide user check, and the confidence ratings.

Three rules from that guide restated here because they carry the whole skill:

  • Confirm the target of the praise. Screen capture shows what was on screen. A glowing comment might be praise for a competitor the user was researching, for a peer, or for a post the user shared but did not write. Adjacency on screen is not a confirmation. Drop or ask (references/evidence-standards.md, rule 4).
  • Take meeting attribution from the summary, never the transcript. Raw transcript chunks are weakly diarized and frequently tagged [Others], which proves someone said it and not who (references/littlebird-mcp-reference.md).
  • A summary is a paraphrase and can never become a quote. It can tell you a quote exists. It cannot be the quote.

Verify the person's role and company before any title is printed next to their name. A stale title is a small but real credibility hit, and printing a title can convert an ordinary consumer endorsement into an implied expert endorsement with its own requirements (references/ftc-compliance.md). Where a title cannot be verified, publish the name and company with no title rather than a guessed one.

3. Assign the permission tier

Full procedure in references/permission-tiers.md.

Assign from the source channel, mechanically, before anyone reads the quote for quality:

TierSaid whereStatus
Public and reusableA public comment, review, post or recommendation. Test: could a stranger read this without being given access to anything?Usable with attribution. Courtesy heads-up is still good practice, and linking back to the public source is better than copying the text
Private, needs permissionA DM, an email, a private group, a one-to-one threadNot usable until the person says yes. The skill drafts the request
Confidential, do not useUnder an NDA, about a client's private results, or containing figures the person would not want publicDo not use. Do not ask. Asking is its own harm

Unknown channel means confidential, not private. The default on uncertainty is the most restrictive tier. Meeting material starts at confidential and has to be argued down, not up.

Where the permission requirement comes from is worth knowing: not the FTC, which is untroubled by quoting customers, but state right of publicity law, which contemplates written consent obtained first (references/research/distilled-testimonial-practice.md, section 7).

4. Split out the results quotes

Full procedure in references/ftc-compliance.md.

A quote containing a specific claimed outcome is the most valuable thing in the bank and the most legally loaded. It gets pulled into a separate sub-bank and flagged, because an endorsement about performance is read as representing what customers generally achieve, and a "results not typical" disclaimer does not fix that. The Guides address that exact wording and say such disclaimers are unlikely to be effective (references/research/distilled-testimonial-practice.md, section 5).

Three flags, one per results quote:

FlagMeaning
SUBSTANTIATEDThe user can show this result is typical. Publishable
NEEDS EXPECTED-RESULTS DISCLOSUREAtypical, but the user knows what typical is. Publishable only alongside a clear and conspicuous statement of generally expected performance
NOT PUBLISHABLE AS A RESULTS CLAIMThe user does not know what typical is. Neither option above is available

Most quotes for a small business land on the third flag. Say so plainly rather than nudging toward the first. The skill never assesses whether a result is typical. It cannot: computing typicality from a praise bank is circular, because the bank only contains clients happy enough to say something.

5. Draft permission requests, hold them

Full templates in references/permission-tiers.md. Three templates: public courtesy, private permission, and private-with-a-number.

Every draft is held. HELD FOR APPROVAL. NOT SENT. at the top of every one.

No draft offers a benefit. Not a discount, not a gift card, not a free month. Under 465.4 it is prohibited to provide compensation or incentives in exchange for reviews expressing a particular sentiment, and all ten of the FTC's December 2025 warning letters involved incentives tied to positive reviews (references/research/distilled-testimonial-practice.md, sections 3 and 6). The praise here already exists and is not being purchased, so the ask is not itself the prohibited conduct. Keeping money out of the message keeps it obviously clean.

Several of these drafts are written as the user. Check whether a personal voice skill is installed in the session and use it. If none is installed, say so plainly and point at this marketplace's voice creator skills. Never invent a voice profile.

6. Write the gap report

Which client relationships produced no captured praise in the window. That is a prompt to go ask, not a verdict. Report it as "no captured praise found in this window", never as "this client is unhappy" (references/evidence-standards.md, rule 2).

One caution to include wherever the gap report appears: asking for reviews only from customers expected to be positive is review gating and the FTC calls it out directly (references/research/distilled-testimonial-practice.md, section 6). Mining only praise is not gating, because it does not shape who gets asked. Turning the gap report into "only ask the happy clients for a Google review" is.

Retrieval brief

The actual calls. Window defaults to 180 days, swept in three 60-day blocks. Full per-family detail in references/praise-discovery.md.

Praise in threads, one register family per call

search_user_context
  search_queries_messages: [up to 7 phrasings from ONE register family, for example
                            "thank you so much", "I really appreciate",
                            "thanks for turning that around", "grateful for your help",
                            "you saved me"]
  standalone_query:        "Find messages where a client, customer or collaborator expressed
                            satisfaction, gratitude, praise or a concrete result about work the
                            user did for them."
  date_range:              {start: block start, end: block end}
  filters:                 {data_source: "messages"}

Ten families: gratitude, superlative, relief, numeric result, non-numeric result, recommendation and referral, comparison to alternatives, expansion and renewal, in-the-moment reaction, change over time. Roughly ten calls per 60-day block. That cost is the point. One sweeping "find praise" query surfaces the loudest thank-you and misses everything worth banking.

Public praise on screen

search_user_context
  search_queries:   [business name plus "review", business name plus "stars",
                     user name plus "commented on your post", user name plus "recommends",
                     "endorsed you for", company name plus "testimonials"]
  standalone_query: "Find public comments, reviews, ratings, recommendations or posts praising
                     the user, their company, or their work, as seen on screen."
  date_range:       {start: block start, end: block end}
  filters:          {data_source: "snapshots"}

Run as separate calls by surface: review platforms, social, professional, and the user's own owned pages. Every hit goes through references/attribution-verification.md before banking. This is the highest-volume source of false positives in the skill.

Social and app UIs collapse lists, so any roster of who praised the user built from notification capture is partial by construction. Report the named set and the size of the unnamed gap (references/evidence-standards.md, rule 5).

Clients describing outcomes on calls

LB_INTERNAL_SEARCH_MEETINGS
  query:      one topic per call: client name plus "results", "since we started working
              together", "it saved us", "we would not have been able to", "our team loves",
              "I would recommend", "compared to what we had before"
  start_date: window start
  end_date:   today
  limit:      10

Topic lookup uses SEARCH_MEETINGS. A lookup by meeting NAME uses LIST_MEETINGS with name. Using the wrong one is the most common mistake against this server (references/littlebird-mcp-reference.md).

Attribution for anything found in a meeting

LB_INTERNAL_GET_MEETING
  meeting_id: every recorded id from the calls above

Take the speaker from the owner-tagged ## Decisions and ## Action Items blocks and the attendee list from the linked calendar event. Only then, and only to recover exact wording for a line the summary already located and already attributed:

LB_INTERNAL_GET_MEETING_TRANSCRIPT
  meeting_id: the one meeting containing the line

One at a time. Transcripts are long.

The compressed sweep, to find days worth re-querying

search_user_context
  search_queries: ["client praise", "positive feedback", "thank you note",
                   "good news from a client", "referral"]
  date_range:     {start: window start, end: "now"}
  filters:        {data_source: "summaries"}

Prove absence deliberately, per client, for the gap report

search_user_context
  search_queries:          [client name plus "thank", client name plus "great",
                            client name plus "result"]
  search_queries_messages: [contact name plus "thanks", contact name plus "happy"]
  date_range:              {start: window start, end: "now"}

A negative answer here is a real finding. Record the client, the aliases, the queries and the window alongside it.

Read the relevance scores. Items below 3 are omitted by the server entirely, and an item scored 3 never carries a quote on its own without corroboration (references/littlebird-mcp-reference.md). Message items carry a send time that differs from the collection time, and the send time is the date said (references/evidence-standards.md, rule 8).

Empty retrieval

If all five passes return nothing bankable, report the window, the number of calls run, the register families covered, and stop. Do not widen the window silently. Do not substitute a plausible-sounding quote. Do not paraphrase a summary into a quote (references/evidence-standards.md, rule 9).

A run that reports "180 days, 40 queries, no bankable praise found, here is the gap report and here are three people worth asking" has done its job correctly, and for a user who has never collected a testimonial it is more useful than four weak quotes would be.

If retrieval returns material but every item fails attribution, that is not an empty run. Report the Unverified list with the specific resolving action per item. Most of them resolve with one message to someone the user already knows.

Output

A deep run writes two files in the working directory, or the directory the user names.

testimonial-bank.md, persistent across runs. Ten sections in order:

  1. Header: date built, window, queries run, and the not-legal-advice line
  2. Coverage: which sources were swept and what each returned
  3. Usable now: public tier plus granted private tier, sorted by strength
  4. Awaiting permission: private tier, with request status per row
  5. Results sub-bank: every results quote regardless of tier, each with its flag
  6. Confidential, do not use: speaker, date and a one-line reason, without the quote text
  7. Unverified: which of the five attribution questions it failed, and the resolving action
  8. Declined and revoked: so nothing re-proposes them
  9. Gap report: relationships with no captured praise, and who to ask
  10. Method: queries, window, tools

Per-quote columns: id, quote, quote_original, trim_rules_applied, speaker, role, company, date_said, source, receipt, tier, permission_status, permission_record, results_claim, material_connection, confidence, staleness, objection_answered, strength, notes. Full schema in references/quote-formatting.md.

Section 6 carrying no quote text is deliberate. Raw capture does not ship (references/evidence-standards.md, rule 7), and a confidential quote written into a persistent file is a confidential quote that will eventually be pasted somewhere.

permission-requests-YYYY-MM-DD.md, drafts held for approval, one per person rather than one per quote.

Landing page and case study formats are produced only on request, from the bank, per references/quote-formatting.md.

Guardrail

Never edit a quote into something better.

Light trimming for length is acceptable and is defined precisely in references/quote-formatting.md: what may be cut, how each cut is marked, and the eight things that may never be done. Every trim in the bank carries the rule number that authorized it, and the untrimmed original is stored alongside. A trim with no rule number is a defect.

Rewriting is fabrication. It is also an FTC problem: endorsements must reflect the honest opinion of the endorser and cannot convey an implied representation that would be deceptive if the advertiser made it directly, and the FTC's own summary of the Reviews Rule lists AI-generated fake reviews among the prohibited categories (references/research/distilled-testimonial-practice.md, sections 4 and 8). A model that improves a customer's phrasing is generating text and attributing it to a named human. That is the specific failure this skill is most likely to commit, because improving prose is the thing a language model does by reflex.

Three cuts that look harmless and are not: removing a qualifier, removing a timeframe, and joining two things said on different days. "It worked, once we got our own data in order" is not "It worked" (references/research/distilled-testimonial-practice.md, section 8).

This is not legal advice. The regulatory material in this skill is a summary of public sources as of 2026-08-17. Advertising and publicity law varies by jurisdiction and changes. Take anything consequential to a lawyer. The skill flags and explains. It does not clear.

Approval gate

Nothing is published and nothing is sent. The bank is an internal artifact. Permission requests are drafts.

Before any drafted text reaches another person, present the person, the quote, the receipt, and the full draft verbatim rather than a summary of it. Then use AskUserQuestion to offer: send as written, edit first, hold, or drop (references/evidence-standards.md, rule 6). Approving the bank is not approving a publication, and approving the plan to ask is not approving the words.

Where an action would go through another product, Gmail, a CRM, a site builder, those are separate MCP connectors that may or may not be present. List the available tools first. Where the connector is absent, produce a copy-paste block or an import-ready file instead. Never assume a connector exists.

Routine wiring

Offer to create the monthly sweep. Show the user the exact prompt text and schedule below, get approval with AskUserQuestion, then call LB_INTERNAL_CREATE_ROUTINE. Creating it generates a first report immediately, then it runs on schedule. Do not tell the user to go set it up by hand.

title:    Monthly testimonial sweep
schedule: {"frequency": "monthly", "time": "08:00", "month_day": 1}
notifications_enabled: true
email_notifications_enabled: true

Exact prompt text to pass:

You are running a monthly sweep for praise this user earned, so it can be turned into
social proof later in an interactive session. You OBSERVE and REPORT. You do not draft
messages, you do not send anything, and you do not publish anything.

STEP 1. MEMORY FIRST. Before anything else, call LB_INTERNAL_GET_ROUTINE_REPORTS for this
routine with limit 6 and read every past report. Build a list of every quote you have
already reported, every person you have already named, and every relationship you have
already flagged as having no captured praise. You need all of it in step 5. Do not skip
this step. A report that re-reports last month's quotes is a failed report.

STEP 2. WINDOW. Sweep the last 45 days only. Do not widen it.

STEP 3. GATHER. Run narrow separate queries, never one broad query. Praise is expressed in
many registers that share no vocabulary, so vary the phrasing deliberately.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
83
Forks
37
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
testimonial-miner
Source
github.com/legioncodeinc/vibe-coding-tools