This site is a public preview. I'm building it in public ;)
A system that watches a sales territory, notices what changed and decides who is worth spending money on. It researches each person against a grounded corpus of 138 documents, served by its own MCP server, with every document tagged to a driver in a value framework of six drivers and five personas, so each claim traces back to proof. The researcher picks the one driver that is live for this buyer and frames it with MEDDICC. Then it writes outreach a human would send, and stops.
Every stage carries its cost. Each component is described by the role it plays rather than the vendor filling it, so the stack is swappable without the architecture changing.
Colour says what a step is for. Yellow qualifies, cyan is a seam where data crosses into another system, magenta dotted is a gate that can stop the run and green is an action that does the work. Spends marks the steps that cost money, and dashed taps are providers feeding a stage.
driver_ids 1-6.allowed_tools = None. No web, no corpus, no MCP.book stamp or the client raises. One place for the stamp, the timestamp and the retry.| Service | Role | Supplies | Cost | Could be |
|---|---|---|---|---|
| Professional network seat | Registry | The territory. Tagged saved searches are the config file. | Seat | Any source with saved queries and a change feed |
| Network API | Detection, tier 1 | Read access to the seat. Person deltas (changed jobs, posted) and company spotlights (leadership, funding).OAuth bearer; the account id belongs to a connection, not a person, so a reconnect can silently repoint it | Subscription | Any unified network API |
| Company-signal provider | Detection tier 2, enrichment | Company signal endpoints. Contact email on fire. | Credits, cap 25/run | Any company-signal provider |
| Tech-graph provider | Detection tier 2, enrichment | Tech-stack adoption and tagged requisitions. One tagged technology category is the t3 detector. | 10–30 cr/contact | Any tech-graph provider |
| Extraction API | Acquisition | Bulk profile extraction. The only component that creates contact records. | 1 cr = 1 profile | Any extraction API |
| Resilient scraper | Acquisition | Recent authored posts, plus a web-unlocker recovery pass.~25% of pages refuse; recovery reclaims about half of those | Per page | Any resilient scraper |
| News API | Enrichment | 30-day macro news. The sanctioned path; live web search is off in production. | Per query | Any news API |
| HubSpot | System of record | Contacts, companies, the dedup key, the fire-log backend, and the surface humans review on. | Seat | Swappable, but touches every stage |
Also present, narrower in scope: a business register for verified company registration and a document fetcher.
| Where | Model | Job |
|---|---|---|
| agents/triage.py | Claude Haiku | Classify signal strength, gate entry to the expensive chain. The only place a model filters rather than authors. |
| agents/company_research.py | Claude | Firmographics, macro news, tech stack, business model, why now, relevant drivers. Cached 30 days per company. |
| agents/researcher.py | Claude | Select the live value driver, find the angle and proof point, state what could not be established. |
| agents/outreach_assistant.py | Claude | Write the email and social message from typed briefs only. |
| corpus_mcp/embedder.py | BAAI/bge-small-en-v1.5 | Local embeddings for the corpus index. No API, no rate limit, ~130MB cached on first use. |
agents/_claude_runner.pyEvery model call goes through one module. It strips ANTHROPIC_API_KEY from the child environment so subscription auth is the only path, and owns rate-limit retry and transcript persistence. The model becomes a configuration line rather than a dependency scattered across six agents.
Research and writing combined, run two to four workers wide. Contention bites past four on a subscription. This is the largest per-unit cost in the system and the reason five gates sit in front of it.
Every stage must be cheaper than the one after it, or be able to stop the run.
| # | Stage | Implementation | What it does | Cost |
|---|---|---|---|---|
| 00 | Detect | detect_network detect_signals detect_techgraph | Poll tagged searches. Person rows normalise to hashed events. Account pulls fire only on companies newly present since the last roster, so a static universe never re-fires. | free |
| 01 | Coalesce | signal_store | Fold the same story told by three detectors into one fire, keyed on account, class and window. | free |
| 02 | Score | title_gate | Judge on free evidence before any paid call. Three outcomes, because a rigid two-way filter gets exactly the interesting cases wrong. | last free |
| 03 | Acquire | bulk_extract | Deduplicate to unique profiles, cap, extract in one bulk call. Past the cap is deferred, never dropped. | 1 cr / profile |
| 04 | Dedup | dedup_check | Suppress anyone contacted inside 45 days, keyed on the network member id rather than email, because emails change and member ids do not. | free |
| 05 | Tag | hubspot_tag | Create or update the contact across ~53 mapped properties. | API |
| 06 | Stitch | hubspot_backfill_company | Resolve every contact to a company and fill firmographics. Zero unresolvable is the pass condition. | API |
| 07 | Suppress | anti_match_check | Deterministic kill before any AI spend. Targeting deliberately lives in the scoring layer, not here. | free |
| 08 | Scrape | post_scrape | Recent authored posts, only for what survived everything above. Scraping last is the whole point. | per page |
| 09 | Triage | agents/triage | Haiku classifies signal strength and gates Phase B. | small model |
| 10 | Research & write | researcher outreach_assistant | Two passes over typed contracts with pre-fetched corpus context, one value-framework driver and MEDDICC framing. Output lands on the record marked for review. | largest |
Prompt instruction is advisory. A graded bake-off found an evaluator chain produced worse copy than the writer alone. What works is structural, and none of it depends on the model behaving.
_corpus_prefetch.py fetches passages by code and injects them. The agent cannot choose what to retrieve, so it cannot retrieve nothing and improvise.
allowed_tools = None at write time. Everything it can say arrived through a typed field established upstream.
The candidate carries cited_doc_id; the write seam calls corpus_record_citation on the corpus MCP server. The model does not self-report its sourcing.
Where evidence is absent the field stays empty and the brief says so. A blank proof point is workable. A plausible fabricated one is not, and is far harder to catch downstream.
The corpus answers whether a claim is true. The framework answers the harder question: which true thing is worth saying to this person.
A value driver is a business issue the buyer has whether or not your product exists. Not a feature, not a benefit.
A true driver is customer-centric, measurable against a P&L line, and differentiable. Anything failing those three is a capability wearing a driver's clothes.
The drivers belong to the seller each instance is built for, so they are described here by shape rather than content.
driver_idsThe 138-document index is tagged 1 to 6, so retrieval scopes to the driver the researcher selected. Grounding and framing share one vocabulary.
ResearchBrief carries driver id, name, confidence and grounding. A weakly grounded driver is a visible field, not prose.
Recognise which driver is live in front of this buyer and run MEDDICC around that one. Reciting the catalogue is the failure the framework exists to prevent.
Each driver carries verified customer proof with a source URL. The writer pairs driver to proof; the citation is recorded mechanically.
Five personas recur, and the same driver lands differently in each seat. A finance leader hears money returned to budget. A platform leader hears whether it constrains the roadmap. An engineer hears zero impact on the work and least privilege. Same outcome, three sentences.
This is the part that does not port. The architecture is a fortnight. A defensible value model with real proof behind each driver is months, and it is the actual asset.
| Contract | Fields | From | Carries |
|---|---|---|---|
| SignalEvent | 11 | detectors | Class, source, date, headline, url, and the sha256 that is its identity for deduplication |
| IngestResult | 25 | ingest/orchestrator | Contact and company properties, trigger type and date, recent posts, quotable fragments, signal history |
| CompanyBrief | 35 | company_research | Firmographics, macro news, tech stack, hiring, business model, why now, relevant drivers, vendor relationship |
| ResearchBrief | 27 | researcher | Angle, proof point, driver id and confidence, buyer hypotheses, conversation starters, stated limitations |
| OutreachCandidate | 23 | outreach_assistant | Subject, email body, social draft, angle strength, hook type, review verdict, cited doc id |
Contracts are where silent failure gets caught. A renamed field with no alias does not throw, it validates to empty and the value disappears. That failure mode has cost this system real money.
Each is the scar of a specific incident.
A scanner using grep -I skipped binaries, so it had never opened a PDF or a docx, and called a tree clean while thirteen files in it were contaminated. Any check must see what it checks, and fail loudly when it cannot.
A PDF library imported inside a bare except turned a missing dependency into empty text, and empty into a pass. Dependencies now fail at module load.
Two title heuristics drift. Two contamination guards drift, then disagree about what they can even see. Where a rule is used twice it is called, never reimplemented.
1.6GB parked in a directory the scanner ignored let the tree report clean. Exclusions are printed every run, so an exclusion is a visible decision rather than an assumption.
Every stage that can spend defaults to preview and reports what it would have cost. --confirm is the only spender, and it belongs to a human.
Errored contacts written into the completed list make a resume read as a clean no-op. Failure and completion are different states and must be stored as different states.
Tagged saved searches → network, company-signal and tech-graph detectors → signal_store coalesce on (account, class, 45d) → title_gate scores before spending → bulk extraction of unique profiles under a cap → dedup, tag, stitch, suppress, filter → scrape → Haiku triage → researcher against a driver-tagged corpus → writer with no tools → hubspot_writer single path → human review
Left to right it is a funnel. Right to left it is a cost ladder, each rung cheaper than the one above. Both readings are the same design.