RECON
WindowAug 25 to 26
Detection: 2,932 companies scanned for a signal that is an absence
The companies a Stripe partner manager most needs to reach are invisible in the CRM, because nobody has integrated them yet. The signal I wanted is a company with real traction and no visible payment processor. That signal is an absence, and an absence is the easiest thing in GTM data to get wrong.
A confirmed absence is worth more than a confirmed presence, because presence is what everyone else is already selling to.
Stage log
Build log
Aug 25
Pre-flight against the live table
Audited the inherited table against its live state instead of the handoff. Found eight action columns with no run condition, one company whose website field has no TLD, and a latent scoring bug that would matter later.
Aug 25
Load, with the pre-filter dropped
Pulled every Active YC company with a website, Winter 2021 through Spring 2026. 2,932 rows. The previous build pre-filtered on headcount as a maturity proxy; I dropped it so disqualifiers in the table would do the cutting, instead of a filter deciding in advance what could qualify.
Aug 25
300-row sample, predictions registered first
Stratified on headcount by batch era with a fixed seed, and cost predictions written down before the run. Measured 2.0 executions and 4.0 credits per row. I also called the rate at 3.0 mid-run before the last row landed, which taught me that executions are charged at dispatch, so reading cost off a part-complete run overstates it.
Aug 25
The fallback scraper, tested and rejected
Tested Firecrawl before wiring it in. It cost 4 to 6 credits per domain against a planned 1, failed 5 of 15 serial calls, flipped verdicts with prompt phrasing, and invented a price quote from a page that returns a 404. Rescue rate after a self-consistency rule: 0 of 10. It did not ship.
Aug 26
Full run, predicted before bought
2,881 rows enriched through the table. Predicted 11,524 credits and 5,762 executions. Actual: 11,518 and 5,756. The signal model landed within five rows of a pre-registered prediction on a 2,881-row run.
Aug 26
The bug I called unreachable fired once
In the sample I wrote that a scoring bug was real as written but unreachable in practice. At 2,931 rows it fired exactly once: maximum weight, on a domain that is not the company's website, from a scan that returned nothing. Fixed to zero, and the superseding note is written back into the original results file.
Timing sheet
Metrics
- YC companies loaded
- 2,932
- Scored above zero, the deliverable
- 909
- Credits, predicted vs actual
- 11,524 vs 11,5185,762 vs 5,756 executions. Cost was predicted before it was spent.
- UNCLEAR rate, full run
- 59.2%Reported, and it rises with company youth. The instrument is weakest where the thesis is strongest.
- Evidence URL coverage
- 620 of 909199 of the 609 top-scoring rows carry an absence claim with no clickable citation. Named in the writeup as the weakest part of the deliverable.
- Stripe-only companies showing metered pricing
- 201 of 433Source: working sessionThe first draft said 24 of 51. Those were sample numbers carried into a full-pool claim without being recomputed. The draft was wrong; this is the corrected pair.
Source: working sessionNumbers marked with a dot come from the working session and its published posts. Unmarked numbers are stated in the build artifacts on disk, with file and line.
CO-DRIVER'S NOTEBOOK
The memory layer
So the scan writes snapshots to Supabase through a Deno edge function, and change becomes an event with a date attached instead of an untestable claim. The seeded test produced exactly three change events, at 90, 11 and 45 days, and zero events on 28 first observations, which is the least interesting result to look at and the most important one to get right. Five days of live drift moved 14 of 50 rows.
Clay could not remember. It holds one observation from one day, so it answers what is true now and never what changed. My second-strongest signal is a processor that was not detectable ninety days ago, which is not a property of a company. It is a property of two observations.Onboard: building the memory layerA screen recording made during the build.Opens in a new tab
INCIDENT REPORT
What broke
A billing-category run returned a 49.3% opportunity rate: 36 of 73 companies read as payments-closed but billing-open. Then I killed my own number. Zero billing vendors detected across 73 domains whose scans succeeded, and zero detections of Paddle, Chargebee or Recurly across 300 domains, through the same mechanism that found Stripe 56 times. The scanner reads the client side. Server-side billing never renders there, so a NONE for that category was structurally guaranteed. The 49.3% measured BuiltWith's blindness rather than the market.
The guard I wrote afterward is the reusable lesson: run the vendor list against companies known to use those vendors first. If detection is zero, the category is unmeasurable and NONE means nothing. That check is cheap, and it would have caught this before 146 executions.
Two of the model's five signals were cut, and the cuts are named in the deliverable. Hiring intensity died because the jobs provider returned zero across 71 attempts; a controlled retest showed the zero was the instrument, a title filter matching 8.4% of postings. Capital window was cut on budget rather than evidence, which is the honest reason.
The number measures the instrument.
STAGE END
Debrief
Every failure in this stage had the same shape: a thing that did not happen presenting as a thing that did. The four-state detection design exists because Clay cannot tell we looked and found nothing apart from this input is unset, and the signal I care most about is an absence.
What broke taught me more than what worked.
Named rather than hidden, because a list you can defend includes the parts you could not build.