agentleFS
localoy-ailocaloy-ai/localstack/lead-qualify/SKILL.md

lead-qualify

Skill2 starsChanged 41 days agoMITView on GitHub

Check & fill a lead list — yours or one /lead-search built. Re-checks each row against the open web, fills the gaps it can actually observe (website, decision maker, contact channel), and keeps or cuts each row with a reason and fresh evidence. Never a score. (localstack)

3 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 2 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every row gets keep or cut, a reason, and a fresh evidence URL — never a score.

    line 116
  2. Very specific:

    Pick the topic: If PLAN-*.md files exist, use the one the user named, or the only one, or ask which — or start a new topic when the file is …

    line 126
  3. Very specific:

    Nothing is dropped: Every column that fits nowhere goes into Notes as <column>: <value>.

    line 142
  4. Distinctive:

    Fresh evidence or no verdict: Every keep and every cut cites a URL observed THIS session.

    line 172
  5. Very specific:

    cut: when one failed (the reason names it).

    line 191
  6. Very specific:

    Every verdict and every filled value a reader can check by opening one URL, and every quote in a check is on its page (web_extract find …

    line 293
  7. Distinctive:

    No keep with a failed or unknown check.

    line 295

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

When to invoke this skill

Check & fill. Takes a lead list — one /lead-search built, or a file the user brings — and works through it row by row against the open web:

  • Check: does the website resolve and belong to the company, does the decision maker still match a fresh search, does any plan disqualifier fire.
  • Fill: blanks and UNKNOWNs it can actually observe — website, location, decision maker, title — and each lead's contact channel, so drafting can start straight away.

Every row gets keep or cut, a reason, and a fresh evidence URL — never a score. Use after /lead-search, when someone shares a list ("here's my list", "enrich this", "check my CSV"), or when asked to "qualify", "verify", or "clean up" a lead list.

Bringing your own file

A shared list comes in whatever shape its author used ("Name, Company, Email, Notes"). Import it before checking:

  1. Pick the topic. If PLAN-*.md files exist, use the one the user named, or the only one, or ask which — or start a new topic when the file is a different piece of work. If none exists, do not send the user to /lead-plan: name the topic from the file and the conversation (<niche>-<territory>, or the file's name), and start a short PLAN-<topic>.md — # Plan: <topic>, then ## What we sell, ## Who buys (ICP), ## Territory and ## Disqualifiers, filled from what the user said and UNKNOWN — ask before drafting otherwise, then the ## Steps checklist with search ticked as "imported <file>".
  2. Map the columns by meaning onto the list's header: Company Name,Location,Website,Decision Maker Name,Title,Profile URL,Evidence URL,Confidence,Verdict,Verdict Reason,Verification URL,Verified Date,Channel,Channel Evidence,Reached,Reached Channel,Exported,Notes "Company"/"Business" → Company Name, "URL"/"Domain" → Website, "Contact"/"Name" → Decision Maker Name, "Role" → Title, "LinkedIn" → Profile URL, an email column → Channel (email <address>, with Channel Evidence = user-supplied). Show the mapping in one short list before writing, and ask only if a column is genuinely ambiguous.
  3. Nothing is dropped. Every column that fits nowhere goes into Notes as <column>: <value>; ….
  4. Imported rows are the user's facts, not observations. Evidence URL = user-supplied, Confidence = unconfirmed until this run checks them.
  5. Merge, never duplicate. A row whose canonical domain is already in leads-<topic>.csv is merged: blanks filled from the file, nothing overwritten, the difference noted in Notes. A lead is already handled when its canonical domain (strip www., lowercase) has a Reached or Exported value in any leads-*.csv in this folder, or appears in a lead-<domain> brain record (localbrain list --type lead with a shell, else .brain/context.md if it exists). Such a row is still imported, but marked in Notes as "already reached or exported" and never queued for drafting.

Say how many rows came in, how many merged, and how many were already handled.

What you read first

  • The topic (no file brought): Pick the topic: ls PLAN-*.md 2>/dev/null. If the user named a topic, use PLAN-<topic>.md. If exactly one PLAN exists, use it. If several exist and the request does not say which, ask the user which one (list them). If none exists, stop and say: "No plan here yet — run /lead-plan first, or bring your own list to /lead-qualify." The topic is the part between PLAN- and .md; every other file for it uses the same topic: leads-<topic>.csv.
  • The list: leads-<topic>.csv (after an import, if there was one). No list and no file → offer /lead-search, or ask for a CSV path or pasted rows. Never fabricate input.
  • The plan: its ## Disqualifiers section becomes the cut criteria. No disqualifiers written → check on observability alone (dead domains, aggregator-as-website, decision maker mismatch) and say so in the report.
  • DESIGN.md, if present — channels to use or avoid; a channel it rules out is not recorded as the lead's Channel.

By default only rows with an empty Verdict are checked, so a rerun picks up just the rows added since. Re-check every row only when the user asks.

Ground rules (non-negotiable)

  1. Fresh evidence or no verdict. Every keep and every cut cites a URL observed THIS session. The original row's evidence proves what was true then; checking is about now.
  2. Keep/cut + reason, never a score. A number invented to rank rows is a fabrication with extra steps. The reason sentence does the ranking.
  3. Cut rows stay in the file. The list keeps every row, its Verdict filled — the cut list is as useful as the keep list.
  4. Fill only what you observed. A blank or UNKNOWN becomes a value only when you read it this session, cited. Never guess an email pattern (first@domain.com is fabrication), never construct a profile URL, never "resolve" UNKNOWN by inference.
  5. Open what you cite. A claim rests on a page you opened this session, never on a search snippet or a memory. Read several pages per web_extract call. Gated sites (LinkedIn and the like) only through search results — and a profile URL only when a search returned it; never construct one.
  6. One check per disqualifier, quoted. For every bullet in the plan's ## Disqualifiers, record pass, fail or unknown, with the URL and words copied exactly from that page. keep only when every one passed; cut when one failed (the reason names it); UNKNOWN when any is unknown — never "relaxed" to a keep. Before writing a keep, prove its quotes: web_extract with find set to them (find_only: true); a quote the page does not contain is not evidence — fix the check or the verdict. Put the checks in Notes: checks: 1 pass, 2 pass, 3 fail — <url>.
  7. People and titles as stated. A title is copied from a page that says it ("Owner", "Practice Manager"); never inferred (a founder is not the owner unless a page says so). Prefer sources from the last two years; a name only found in an older source gets Confidence likely and the year in Notes. A second contact needs its own source.
  8. Partial is partial. Budget spent at row 30 of 50 → the report says 30 checked, 20 unchecked, and the unchecked rows keep verdict UNKNOWN.

Procedure

1. Load — import the user's file first if there is one (above), then the list and plan (stage load above is the spec).

Scratch for this run lives in .localstack/work/{date}-{slug}/ — hidden, one directory per run, so a new run never clobbers an earlier one and the folder's top level stays the standard files. Scratch is disposable; old run directories may be deleted freely.

The bar for keep (the user's, 2026-10-03): the row fits the plan exactly — every disqualifier checked and none broken; anything you found that breaks one (acquired, over the size, outside the window, not independent) is a cut, even if the rest is strong — and the person is likely to reply: the right role at that size, with a real channel you observed. No channel, or the wrong person, is a cut or UNKNOWN, never keep. Fewer kept rows that meet this beat a full list that does not.

Close, not padded. A row that misses the bar by one thing — the rival is adjacent rather than head-to-head, a little over the size, no channel found yet, the round just outside the window — is close, not keep and not cut. Its Verdict Reason starts with that one difference ("Close: 230 people, plan says up to 200"). Close rows are shown to the user as their own group; they decide which to promote (set to keep) or which rule to loosen. Drafts and exports use keep rows only. Two or more differences is a cut.

Sort the queue first, when you have a decide tool. One decide call over leads-<topic>.csv (fields: Company Name, Location, Website, Title, Notes, Evidence URL) with one yes/no question per Disqualifier — "Does the row's own text show it breaks this rule: <the rule, word for word>?" — and the plan's size and territory as context. A row with a sure yes (not marked ?) is checked first and only for that rule, on its Evidence URL: confirmed and quoted, it is a cut; not confirmed, it gets the full check below. decide never keeps a row and never cuts one on its own word: it only orders the work. No decide tool: skip this.

2. Check and fill each queued row (stage verify is the spec). With a task tool, give the rows to helpers, 3 to 5 rows each, up to 6 at once — the rows in the brief, read: the plan file, done_when: every row has a Verdict with its evidence, returns: the finished rows — then write the returned rows in place: website is their own domain and alive; every disqualifier checked on a page you opened, quoted (rule 6); the decision maker and title read from a page that states them (rule 7); an observed contact channel found where one exists; blanks filled only from observations. Write each row's result into leads-<topic>.csv as soon as it is checked — a long run never holds its findings in memory or scratch only.

3. Write the results in place.

  • leads-<topic>.csv — on each checked row, fill Verdict (keep, cut or UNKNOWN), Verdict Reason, Verification URL, Verified Date, and Channel / Channel Evidence when a channel was observed; replace blanks or UNKNOWNs only with observed values. The row count and order never change.
  • PLAN-<topic>.md — add a dated section after ## Steps:
## Qualify — YYYY-MM-DD
kept N / cut M — cuts by reason: <reason>: n, ...
filled: F values (websites w, decision makers d, channels c)
no observed channel: (kept rows without one), or "none"
could not verify: (rows left UNKNOWN and why — budget, ambiguity, dead
trails), or "none"

Standard files. This folder is kept in files any agent already reads. Update them in place; never scatter output into new folders.

  • AGENTS.md — create it if missing. localstack owns only the block between <!-- localstack:start --> and <!-- localstack:end -->; rewrite that block, never anything outside it. The block says what this folder is for, the rules (drafts only; nothing is sent without the user's explicit yes, one message at a time; no invented facts), a map of the files below, and one line per topic (its PLAN, its lead count, the next unticked step) and per report (its file and date).
  • PLAN-<topic>.md — in ## Steps, tick - [x] qualify (add today's date after it) once this step is done; leave it unticked if you stopped partway, and say why in CHANGELOG.md.
  • CHANGELOG.md — create it if missing (# Changelog). Add one bullet for this run under today's ## YYYY-MM-DD heading, newest date first: the skill, the topic, and the counts or outcome (e.g. - lead-search austin-dentists: 18 found, 3 skipped as already contacted).
  • TODOS.md — create it if missing (# TODOs). Add each open next action as - [ ] <action> (<topic>); tick items this run finished; never delete lines.
  • DESIGN.md — decisions meant to last (positioning, tone, channels to use or avoid). Read it before writing anything a person will see; add to it only when the user states or approves a decision.

For this step: the CHANGELOG line gives kept / cut / filled counts (and "imported N rows from <file>" when there was an import); add - [ ] draft outreach for N kept leads (<topic>) to TODOS.md, and one TODO per kept row with no observed channel.

4. Report and hand off. In chat: the strong (keep) rows, then the close rows as their own group with each one's difference, then the cut count — and ask once whether to promote any close rows or loosen the rule most of them miss. Then: kept/close/cut counts, what was filled, the dominant cut reasons, what could not be checked. Then offer the next stage — "Draft outreach for the kept rows with /lead-draft?" — as a structured question where the runtime supports one, plain text otherwise. On yes, invoke /lead-draft if this runtime can invoke skills directly (Claude Code: the Skill tool); otherwise tell the user to type /lead-draft (Codex: $lead-draft).

Quality bar

  • Every verdict and every filled value a reader can check by opening one URL, and every quote in a check is on its page (web_extract find said so).
  • No keep with a failed or unknown check.
  • A cut without a reason, or a keep without fresh evidence, is a failed row — fix it or mark it UNKNOWN.
  • An imported file loses nothing: every source column is in a header column or in Notes.
  • The verdict distribution is honest: a pass that keeps everything checked nothing.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.