← Back to tools

Open source · MIT

View on GitHub ↗

Signal Monitor — typed buying-window signals with code-enforced clamps

LangGraph agent that watches a watchlist of companies for six typed buying-window signals and emails you a weekly digest. Three code-enforced clamps (evidence, spend, dedup) make it safe to leave running unattended. Companion to the Account Research Agent — modular, not coupled.

$0.56

first 5-company run (live)

5/5

offline replay tests passing

MIT

open source license

LangGraph Anthropic SDK Pydantic Exa Tavily Perplexity Claude Sonnet Claude Haiku SQLite Resend launchd Python

The problem

ICP-fit research tells you who fits. It does not tell you when to act. The buying window is a moving target — a Series B closed last week, a new VP Sales hired yesterday, an open GTM Engineer JD posted four days ago, a public statement that the company is evaluating Clay. Most “AI for sales” tools surface these as scraped noise: undated, unverified, attributed to URLs that may or may not contain what was claimed. The Account Research Agent answered who. This tool answers when.

What it does

Input: a YAML list of company domains. Output: a weekly email digest of dated buying-window signals — every signal verified against its live source URL by code before it ships.

Six typed signal classes, each with its own primary source, recency window, and identity-key contract for dedup:

TypePrimary sourceRecency windowWhy it matters
funding_roundExa120 daysCash-in opens a tooling spend window within 60-90 days
exec_moveExa90 daysNew CRO / COO / Head of RevOps has a 90-day mandate to rebuild the stack
relevant_jdTavily45 daysThey’re admitting they don’t have the capability internally yet
product_launchPerplexity60 daysGrowth pressure triggers outbound + RevOps re-tooling
tech_signalExa30 daysThey’re touching the GTM stack right now
news_strategicPerplexity60 daysM&A, IPO file, layoffs, reorg — priorities and tooling realign

Anything that doesn’t fit one of the six types gets dropped. The taxonomy is the contract.

The architecture is a LangGraph state machine: six producers fan out as a parallel super-step, the critic runs an evidence-traceability check in code before any LLM judgment, the orchestrator selectively re-fires failed producers via Anthropic’s native web_search fallback, the deduper writes survivors to SQLite with per-type recency windows so signals don’t re-surface week-over-week.

Architecture

Architecture diagram of Signal Monitor: a watchlist enters the loader, which dispatches to six producers running as a parallel super-step (funding_round, exec_move, relevant_jd, product_launch, tech_signal, news_strategic). Their writes merge into the critic, which runs a code-enforced evidence_traceable clamp followed by three LLM-judged dimensions. The orchestrator routes via LangGraph Command — either Send to specific failed producers with a web_search fallback hint, or done to the deduper. The deduper checks identity_keys against SQLite with per-type recency windows. Survivors hit digest_compiler, which formats and emails via Resend.

Yellow nodes mark the code-enforced clamps (evidence_traceable in the critic, identity_key dedup in the deduper). The blue diamond is the orchestrator’s routing gate — Command(goto=Sends) for selective re-fire, or straight through to the deduper when no gaps remain or the retry budget is spent. The green node carries the parallel super-step where all six producers fire concurrently and their writes merge via the merge_signals reducer.

Architectural decisions worth defending

1. Three code-enforced rules, not three prompt-asked-nicely rules. The agent has three commitments that can’t be talked around because they live in Python, not prose:

  • evidence_traceable — the critic fetches each signal’s source_url, renders it to text, and substring-matches the verbatim evidence_quote. Hallucinated quotes attributed to real URLs score 0 and get dropped. Same architectural move as the disqualifier clamp in the Account Research Agent.
  • Spend cap — a SpendTracker wraps every API call, charges optimistically, and raises BudgetExceeded before the next call would push the run total over the per-run cap. Per-company soft cap stops one bad domain from starving the rest.
  • Identity-key dedup — each signal type owns its own identity_key() method (e.g. funding_round keys on (company, round_type, year_month)). Same Series B reported across five outlets = one identity. Stored in SQLite, scoped by per-type recency window, so a 6-month-old funding round naturally exits the active dedup set and stops surfacing.

2. Critic-driven selective re-fire via LangGraph Command(goto=Sends). When a signal fails the critic, the orchestrator doesn’t re-run everything. It emits a Send to the specific producer that owns the failed type, with a hint to use the Anthropic native web_search fallback. Other producers stay put. Same Send pattern as the Account Research Agent’s Gap.target_node, applied to source-affinity rather than gap-affinity.

3. Modular, not coupled. The Account Research Agent answers “who fits and why” once per company. Signal Monitor answers “when to act on the fit you already validated” weekly per company. The two compose as a pre-outbound stack — but neither depends on the other. Signal Monitor’s watchlist contract is a flat YAML, not an ARA brief, so forkers can run it standalone.

First live run

Five-company watchlist (Vercel, Retool, Anthropic, Clay, Apollo) on 2026-06-06:

Subject: Signal Monitor — 6 new signals across 3 companies

VERCEL.COM [relevant_jd] Open req: GTM Engineer (US Remote) (confidence: 0.95) Why this matters: Open GTM Engineer req at Vercel = internal gap acknowledged. Position yourself as the ready-now architect before a 9-month hire fills it.

APOLLO.IO [relevant_jd] Open req: GTM Engineer (Remote, US) (confidence: 0.95) Why this matters: Apollo is hiring exactly the role you fill — position yourself as the practitioner they’re trying to replicate internally.

ANTHROPIC.COM [exec_move] Hire: VP Sales (confidence: 0.85) Why this matters: New segment-specific sales leader = active GTM motion build. Hook: position your stack as the infrastructure she needs to ramp fast.

(… 3 more signals …)

Watchlist: 5 companies · Signals this week: 6 · Run cost: $0.56 / $4.00 cap

Every quote in every signal was verified against its live source URL by the evidence_traceable clamp before the digest was sent. Total wall-clock: ~90 seconds.

Eval discipline

Offline replay tests run on every commit and include a deliberate hallucination-bait snapshot — same HTML page as a real funding announcement, but with a fabricated evidence_quote swapping the round letter, dollar amount, and lead investor. The clamp scores it 0 and the critic rejects it. If that test ever goes green by accident, the architectural commitment is broken.

$ pytest evals/replay/ -v
test_evidence_check_snapshot[exec_move_positive.expected.json]        PASSED
test_evidence_check_snapshot[funding_round_hallucinated.expected.json] PASSED  ← the load-bearing test
test_evidence_check_snapshot[funding_round_positive.expected.json]     PASSED
test_evidence_check_snapshot[relevant_jd_positive.expected.json]       PASSED
test_evidence_check_snapshot[spa_shell.expected.json]                  PASSED
5 passed in 0.06s

Part of the pre-outbound stack

This is the second repo in a three-stage agentic pre-outbound system. Each repo runs standalone; together they compose end-to-end.

RepoAnswersOutput contract
Account Research AgentWho fitsJSON via python run.py <domain> --json
Signal Monitor (this tool)When to actJSON via python run.py --only <domain> --json
Meeting Prep AgentWhat to sayMarkdown + JSON sidecar via python run.py <input.yaml>

Downstream, Outbound Campaign Crew (CrewAI) extends the stack into outbound generation — claim-traceable sequences gated by ARA’s verdict.

And the discipline layer above all of them:

RepoRole
eval-watchHow do you know your agents — all of them — are right? Monthly GH Actions cron, regression gate vs anchored baseline, STATUS.md auto-committed to the repo.

Three MIT repos. Three architecturally-coherent state machines. One thesis: code-enforced rules over prompt-asked-nicely rules, critic-driven self-correction, modular composition through stable CLI contracts. Same pattern shows up seven times across the portfolio: disqualifier clamp (ARA) → length compliance (sister project, the blog autopilot) → three clamps (here) → concurrency clamp in this runtime → four clamps + sequential composition in MPA → eval discipline enforced in CI (eval-watch) → Flow gates + task guardrails in CrewAI (Outbound Campaign Crew).

Use it

The agent is MIT licensed. Bring your own API keys (Anthropic + Exa + Tavily + Perplexity + Resend). Edit watchlist.yaml with the companies you want to watch. Install the launchd autopilot and the digest hits your inbox every Saturday at 7am.

git clone https://github.com/aehirota/signal-monitor
cd signal-monitor
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env  # fill in API keys
python run.py --dry-run    # smoke test — prints digest to stdout
./scripts/load-launchd.sh  # weekly autopilot

Architecture deep-dive in docs/architecture.md. Honest limitations in KNOWN_ISSUES.md — the clamp is strict by design, which produces predictable false negatives on JS-rendered pages and anti-bot-protected sites. The trade-off (recoverable false negative over unrecoverable false positive) is documented with real examples from the first live run.

Let's talk

Let's build your revenue system

I'm available for GTM engineering and RevOps projects. Tell me where revenue is leaking and I'll tell you what I'd build.