Signal scanner

Detect buying signals across TAM companies and watchlist personas.

How to use it

  1. Hit Copy SKILL.md — or use the Claude Code line below to get every file.
  2. Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
    ChatGPT: make a Project and paste it into Instructions.
    Neither? Paste it at the top of a new chat — it works for that chat.
  3. Describe your job in plain words. The AI follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit gooseworks-ai/goose-skills/skills/lead-generation/capabilities/signal-scanner#main ~/.claude/skills/signal-scanner

For one project only, change the path to .claude/skills/signal-scanner. This skill also uses example.json, signal_scanner.py — copying SKILL.md alone won't be enough. See the folder on GitHub.

Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Show the full text139 lines
signal-scanner/SKILL.md139 lines5.5 KBpushed 96d agoRawView on GitHub

Signal Scanner

Scheduled scanner that detects buying signals on TAM companies and watchlist personas, writes them to the signals table, and sets up downstream activation.

When to Use

  • After TAM Builder has populated companies and personas
  • As a recurring scan (daily/weekly) to detect timing-based outreach triggers
  • When you need to move from static lists to intent-driven outreach

Prerequisites

  • SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY in .env
  • APIFY_TOKEN in .env (for Phase 2 signals)
  • ANTHROPIC_API_KEY in .env (optional, for LLM content analysis)
  • TAM companies populated via tam-builder
  • Watchlist personas created for Tier 1-2 companies

Signal Types

Priority Signal Level Source Cost
P0 Headcount growth (>10% in 90d) Company Data diffs Free
P0 Tech stack changes Company Data diffs Free
P0 Funding round Company Data diffs Free
P0 Job posting for relevant roles Company Apify linkedin-job-search ~$0.001/job
P1 Leadership job change Person Apify linkedin-profile-scraper ~$3/1k
P1 LinkedIn content analysis Person Apify linkedin-profile-posts + LLM ~$2/1k + LLM
P1 LinkedIn profile updates Person Apify linkedin-profile-scraper ~$3/1k
P2 New C-suite hire Company Derived from person scans Free

Config Format

See configs/example.json for full schema. Key sections:

  • client_name — which client's TAM to scan
  • signals.* — enable/disable each signal type with thresholds
  • scan_scope — filter by tier, status, lead_status

Database Write Policy

CRITICAL: Never write signals or update lead statuses without explicit user approval.

The signal scanner writes to multiple tables: signals (insert), enrichment_log (insert), companies (patch snapshots), and people (patch lead_status). These writes affect downstream outreach decisions — bad signals lead to bad outreach timing.

Required flow:

  1. Always run --dry-run first to detect signals without writing to the database
  2. Present the dry-run results to the user: signal count, types, top signals, affected companies/people
  3. Get explicit user approval before running without --dry-run
  4. Only then run the actual scan that writes to the database

Why this matters:

  • Signals drive outreach timing — incorrect signals trigger premature outreach
  • lead_status changes from monitoring to signal_detected are hard to undo across many records
  • Snapshot updates affect future signal diffs — bad snapshots cascade into future scans
  • Enrichment log entries track Apify credit spend

The agent must NEVER pass --yes on a first run. The --yes flag is only for pre-approved scheduled scans where the user has already validated the signal detection logic.

Usage

# Dry run first (ALWAYS DO THIS) — detect signals without writing to DB
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
  --config skills/capabilities/signal-scanner/configs/my-client.json --dry-run

# Full scan (only after user reviews dry-run results and approves)
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
  --config skills/capabilities/signal-scanner/configs/my-client.json

# Test mode (5 companies max)
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
  --config configs/example.json --test --dry-run

# Free signals only (skip Apify)
# Set all Apify signals to enabled: false in config

Flags

Flag Effect
--config PATH Path to config JSON (required)
--test Limit to 5 companies, 3 people
--yes Auto-confirm Apify cost prompts. Only use for pre-approved scheduled scans.
--dry-run Detect signals but don't write to DB. Always run this first.
--max-runs N Override Apify run limit (default 50)

Output

Signals table writes

Each signal includes: client_name, company_id, person_id, signal_level (company or person), signal_type, signal_source, strength, signal_data (JSON), activation_score, detected_at, acted_on, run_id.

Other database writes

  • Person lead_status updated to signal_detected when activation_score >= threshold
  • Company metadata._signal_snapshot updated for next diff cycle
  • Person raw_data._signal_snapshot updated for next diff cycle
  • enrichment_log entries with tool='apify', action='search' or 'enrich', plus credits_used

Console output

  • Summary stats printed to stdout

Activation Score

activation_score = strength * recency_multiplier * account_fit

Recency:   <24h = 1.5, 1-3d = 1.2, 3-7d = 1.0, 1-2w = 0.8, 2-4w = 0.5
Account:   Tier 1 = 1.3, Tier 2 = 1.0, Tier 3 = 0.7

Connects To

  • Upstream: tam-builder (provides companies + people)
  • Downstream: cold-email-outreach (acts on signals)

File Structure

signal-scanner/
├── SKILL.md
├── configs/
│   └── example.json
└── scripts/
    └── signal_scanner.py
1---
2name: signal-scanner
3description: >
4 Detect buying signals across TAM companies and watchlist personas.
5 Three-phase architecture: (1) free diff-based signals from existing data
6 (headcount growth, tech stack changes, funding rounds), (2) Apify-powered
7 signals (job postings, LinkedIn content analysis, profile changes), and
8 (3) post-processing with dedup, scoring, and lead status updates.
9 Writes signals to Supabase signals table for downstream activation.
10tags: [lead-generation]
11---
12 
13# Signal Scanner
14 
15Scheduled scanner that detects buying signals on TAM companies and watchlist personas, writes them to the `signals` table, and sets up downstream activation.
16 
17## When to Use
18 
19- After TAM Builder has populated companies and personas
20- As a recurring scan (daily/weekly) to detect timing-based outreach triggers
21- When you need to move from static lists to intent-driven outreach
22 
23## Prerequisites
24 
25- `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` in `.env`
26- `APIFY_TOKEN` in `.env` (for Phase 2 signals)
27- `ANTHROPIC_API_KEY` in `.env` (optional, for LLM content analysis)
28- TAM companies populated via `tam-builder`
29- Watchlist personas created for Tier 1-2 companies
30 
31## Signal Types
32 
33| Priority | Signal | Level | Source | Cost |
34|----------|--------|-------|--------|------|
35| P0 | Headcount growth (>10% in 90d) | Company | Data diffs | Free |
36| P0 | Tech stack changes | Company | Data diffs | Free |
37| P0 | Funding round | Company | Data diffs | Free |
38| P0 | Job posting for relevant roles | Company | Apify linkedin-job-search | ~$0.001/job |
39| P1 | Leadership job change | Person | Apify linkedin-profile-scraper | ~$3/1k |
40| P1 | LinkedIn content analysis | Person | Apify linkedin-profile-posts + LLM | ~$2/1k + LLM |
41| P1 | LinkedIn profile updates | Person | Apify linkedin-profile-scraper | ~$3/1k |
42| P2 | New C-suite hire | Company | Derived from person scans | Free |
43 
44## Config Format
45 
46See `configs/example.json` for full schema. Key sections:
47 
48- `client_name` — which client's TAM to scan
49- `signals.*` — enable/disable each signal type with thresholds
50- `scan_scope` — filter by tier, status, lead_status
51 
52## Database Write Policy
53 
54**CRITICAL: Never write signals or update lead statuses without explicit user approval.**
55 
56The signal scanner writes to multiple tables: `signals` (insert), `enrichment_log` (insert), `companies` (patch snapshots), and `people` (patch lead_status). These writes affect downstream outreach decisions — bad signals lead to bad outreach timing.
57 
58**Required flow:**
591. **Always run `--dry-run` first** to detect signals without writing to the database
602. Present the dry-run results to the user: signal count, types, top signals, affected companies/people
613. **Get explicit user approval** before running without `--dry-run`
624. Only then run the actual scan that writes to the database
63 
64**Why this matters:**
65- Signals drive outreach timing — incorrect signals trigger premature outreach
66- `lead_status` changes from `monitoring` to `signal_detected` are hard to undo across many records
67- Snapshot updates affect future signal diffs — bad snapshots cascade into future scans
68- Enrichment log entries track Apify credit spend
69 
70**The agent must NEVER pass `--yes` on a first run.** The `--yes` flag is only for pre-approved scheduled scans where the user has already validated the signal detection logic.
71 
72## Usage
73 
74```bash
75# Dry run first (ALWAYS DO THIS) — detect signals without writing to DB
76python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
77 --config skills/capabilities/signal-scanner/configs/my-client.json --dry-run
78 
79# Full scan (only after user reviews dry-run results and approves)
80python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
81 --config skills/capabilities/signal-scanner/configs/my-client.json
82 
83# Test mode (5 companies max)
84python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
85 --config configs/example.json --test --dry-run
86 
87# Free signals only (skip Apify)
88# Set all Apify signals to enabled: false in config
89```
90 
91### Flags
92 
93| Flag | Effect |
94|------|--------|
95| `--config PATH` | Path to config JSON (required) |
96| `--test` | Limit to 5 companies, 3 people |
97| `--yes` | Auto-confirm Apify cost prompts. **Only use for pre-approved scheduled scans.** |
98| `--dry-run` | Detect signals but don't write to DB. **Always run this first.** |
99| `--max-runs N` | Override Apify run limit (default 50) |
100 
101## Output
102 
103### Signals table writes
104Each signal includes: `client_name`, `company_id`, `person_id`, `signal_level` (company or person), `signal_type`, `signal_source`, `strength`, `signal_data` (JSON), `activation_score`, `detected_at`, `acted_on`, `run_id`.
105 
106### Other database writes
107- Person `lead_status` updated to `signal_detected` when activation_score >= threshold
108- Company `metadata._signal_snapshot` updated for next diff cycle
109- Person `raw_data._signal_snapshot` updated for next diff cycle
110- `enrichment_log` entries with `tool='apify'`, `action='search'` or `'enrich'`, plus `credits_used`
111 
112### Console output
113- Summary stats printed to stdout
114 
115## Activation Score
116 
117```
118activation_score = strength * recency_multiplier * account_fit
119 
120Recency: <24h = 1.5, 1-3d = 1.2, 3-7d = 1.0, 1-2w = 0.8, 2-4w = 0.5
121Account: Tier 1 = 1.3, Tier 2 = 1.0, Tier 3 = 0.7
122```
123 
124## Connects To
125 
126- **Upstream:** `tam-builder` (provides companies + people)
127- **Downstream:** `cold-email-outreach` (acts on signals)
128 
129## File Structure
130 
131```
132signal-scanner/
133├── SKILL.md
134├── configs/
135│ └── example.json
136└── scripts/
137 └── signal_scanner.py
138```
139 

Discussion

Alternatives

Also in Buying signals