# Buyer Discovery

> Discover evidence-backed B2B buyer organizations and contact actions for a product and target market using agent-authored search strategy, DataForSEO search, Cloudflare crawl evidence, site classification gates, relation judgment, contact extraction, lead action planning, and a read-only Node/Svelte progress UI. Use when Codex needs to find, dedupe, crawl, screen, visualize, or export potential overseas B2B buyer organizations, source sites, contact facts, or outreach actions for a seller.

- Skill: `yansircc/buyer-discovery` (Agent Skill, multi-file: 42 files)
- Install (CLI): `npx skillmds@latest add yansircc/buyer-discovery`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yansircc/buyer-discovery/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: yansircc (https://skillmd.com/u/yansircc)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/yansircc/buyer-discovery

---


# Buyer Discovery

Use this skill to turn a product and target market into evidence-backed B2B buyer actions.

Invariant:

```text
Search discovers surfaces, not organizations.
run.db owns structured facts.
Provider JSONL owns raw API audit only.
CSV files and judge_packets.jsonl are projections over run.db facts.
surface_hits is the surviving discovered surface table.
site_classifications is the promotion gate.
Only official_organization_site rows promote to organizations.
relation_judgments judges economic relation only for organizations.
contacts records contact facts; contact fact != lead action.
lead_actions joins relation + contact ownership + contact intent.
Agent owns semantic strategy and ambiguous business judgment.
Scripts own query compilation, execution, extraction, gates, and persisted facts.
Svelte is read-only ops projection.
```

## Workflow

1. Ask for scope before running when the user has not already chosen it.

Use `request_user_input` or `ask_user_questions` when available. If it is not available, ask directly in plain language and wait.

Question:

```text
你想先快速看一小批结果，还是直接做完整搜索？
```

Options:

```text
先看少量结果 (Recommended): 约 2-5 分钟。先确认方向对不对，结果不是最终名单。
直接完整搜索: 约 30-90 分钟。覆盖更全，但等待时间更长。
```

Do not expose API terms such as SERP, crawl, async, depth, domain dedupe, or relation_type in user-facing questions.

2. Express search strategy before execution.

Create a strategy template, then edit it when product semantics require better buyer roles, traces, local terms, or negative query terms:

```bash
node scripts/buyer_discovery.mjs strategy-template \
  --product "electric skateboards" \
  --target-market "Australia" \
  --out /tmp/strategy.json
```

Agent strategy shape:

```text
product_terms, application_scenes, buyer_roles, public_traces,
source_priorities, market_terms, local_language_terms, negative_terms
```

Validate strategy before any network run:

```bash
node scripts/buyer_discovery.mjs strategy-validate \
  --strategy-file /tmp/strategy.json \
  --product "electric skateboards" \
  --target-market "Australia" \
  --out /tmp/strategy.normalized.json
```

Use the normalized file for execution. The validator fails on malformed JSON, duplicate keys, wrong field types, and unknown `source_priorities`. Do not repair strategy by editing `run-input.json`.

`negative_terms` are query compile inputs only. Script hard-reject blocklists are structural gates.

3. Run the top-level command.

Quick mode automatically searches, filters surfaces, crawls a small time-boxed sample, maps pages, classifies sites, promotes official organizations, judges relation, extracts contacts, and writes lead actions. Do not ask whether to verify after quick mode; quick already does a small site check.

```bash
node scripts/buyer_discovery.mjs quick-scan \
  --product "electric skateboards" \
  --seller-context "China B2B exporter" \
  --target-market "Australia" \
  --strategy-file /tmp/strategy.normalized.json
```

Full mode uses the same pipeline with full search settings and broader crawl targets:

```bash
node scripts/buyer_discovery.mjs full-search \
  --product "electric skateboards" \
  --seller-context "China B2B exporter" \
  --target-market "Australia" \
  --strategy-file /tmp/strategy.normalized.json \
  --confirm-full-search
```

Quick uses DataForSEO Live at low depth and has a default `--quick-budget-seconds 120` time box. Full uses DataForSEO Standard async at full depth. Do not hand-edit `run-input.json` to change scope, path count, depth, or backend.

After quick mode completes, stop and report the partial projection. Never start `full-search` or `set-scope --search-scope full` unless the user explicitly confirms the longer run. Do not promise automatic notification unless a durable automation or thread wakeup was actually created.

4. Report progress while running.

Always report stable milestones in plain language:

- start: exact `run_dir` and expected duration;
- search finished: organic rows and discovered surfaces, not buyers;
- site checks finished or time-boxed: official organizations, source sites, unknown/noise surfaces;
- contacts/actions ready: `lead_actions.csv` path and top rows;
- partial state: `status.partial`, `status.partial_reasons`, and pending provider task count;
- visible failures: `status.error_count`, `status.failed_provider_tasks`, and `status.top_errors`.

If no milestone changes for 30-60 seconds in quick mode, or 3-5 minutes in full mode, run:

```bash
node scripts/buyer_discovery.mjs status --run-dir /tmp/buyer-discovery/<run-id>
```

Vocabulary is strict:

```text
surface_hits = discovered website/page surfaces
organizations = official organizations after site classification
lead_actions = actionable contact recommendations
```

Do not call a row a high-value buyer before it appears in `lead_actions`.

5. Use lower-level commands only for recovery or debugging.

```bash
node scripts/buyer_discovery.mjs init-run --product "electric skateboards" --target-market "Australia" --search-scope quick
node scripts/buyer_discovery.mjs compile-paths --run-dir /tmp/buyer-discovery/<run-id> --strategy-file /tmp/strategy.json
node scripts/buyer_discovery.mjs serp-submit --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs serp-poll --run-dir /tmp/buyer-discovery/<run-id> --wait
node scripts/buyer_discovery.mjs surface-filter --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs crawl-submit --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs crawl-poll --run-dir /tmp/buyer-discovery/<run-id> --wait
node scripts/buyer_discovery.mjs page-map --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs site-classify --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs org-promote --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs judge-pack --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs auto-judge --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs contact-extract --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs lead-actions --run-dir /tmp/buyer-discovery/<run-id>
node scripts/buyer_discovery.mjs run-learnings --run-dir /tmp/buyer-discovery/<run-id>
```

6. Review or override relation judgments only after reading `references/relation-judge.md`.

Every accepted/reviewable relation must cite evidence URLs from official organization pages. Relation judgment must not classify source sites, marketplaces, directories, or content noise; that is the `site_classifications` table's responsibility.

7. Use the read-only UI when useful.

```bash
node scripts/buyer_discovery.mjs ui --run-dir /tmp/buyer-discovery/<run-id>
```

The UI may show discovered surfaces, official organizations, source sites, noise/unknown surfaces, contacts, lead actions, and errors. It must not submit jobs, edit CSV files, or export results.

## Outputs

Every run writes `run.db` as the structured fact store. The authoritative table contract lives in `scripts/src/core/db.ts` migrations; this markdown describes how agents should consume the projections, but it is not the schema source of truth.

Run projections and audit files:

```text
run.db
run-input.json
search_strategy.json or external strategy file
search_strategy.normalized.json
source_paths.csv
serp_tasks.jsonl
raw_serp.jsonl
serp_domains.csv
surface_hits.csv
serp_rejected.csv
crawl_tasks.jsonl
crawl_records.jsonl
pages.csv
site_classifications.csv
organizations.csv
judge_packets.jsonl
relation_judgments.csv
contacts.csv
lead_actions.csv
run_learnings.csv
candidates.csv
rejected.csv
```

`serp_tasks.jsonl`, `raw_serp.jsonl`, `crawl_tasks.jsonl`, and `crawl_records.jsonl` are raw provider audit streams. CSV files are export/projection surfaces for humans and downstream tools. v4 pipeline consumers must not read generated CSV or `judge_packets.jsonl` as fact-path input.

## Mechanical Rules

- Keep API keys in environment variables only.
- Required variables are shown in `.env.example`.
- CLI reads local `.env` / `.env.local` from current and parent directories, or `BUYER_DISCOVERY_ENV=/path/to/.env`.
- Run `node scripts/buyer_discovery.mjs env-status` before network runs.
- Do not hard-reject manufacturers/factories in scripts. Classify economic relation from official evidence.
- Treat blocked/403 evidence as `unknown` at site classification or `insufficient evidence` at relation judgment, not as buyer rejection.
- `surface_hits.priority_score` is a mechanical ordering hint from surface filtering, not lead score.
- `source_paths.executed` is lifecycle state, not path quality.
- `run_learnings.csv` is final reporting only; it must not feed decisions in the same run.
- `state-json` and the Svelte UI read DB-backed projections only; they must not mutate runs.

## References

- Read `references/schemas.md` when editing or consuming run files.
- Read `references/relation-judge.md` before classifying economic relation.

## Validation

```bash
npm --prefix scripts run typecheck
node scripts/buyer_discovery.mjs self-test
npm --prefix scripts run build
```

Then run the skill creator validator from the local Codex skill-creator installation.

