Open source · MIT · Build from source

Prove it before you send it.

Assay grades a business's AI readiness from their own page source. It refuses to build the outreach packet until you have recomputed the findings from source you pasted yourself.

Six checks · MIT licensed · Your Places key, your Anthropic login, no Assay server

$ npm test
preflight · smoke · ipc · parsers
instrument · confirm · packet · approve

Eight stages, one command.
Ryan Lenk, who built Assay and runs the SEO, GEO and AEO practice it was built for

Hey, I'm Ryan LenkI run a one-person SEO, GEO and AEO practice out of Pennsylvania. Assay is the tool I prospect with. More about me.

Eight seconds of it running.

Assay running. Five candidate businesses plotted on a map of the Pittsburgh area with their names redacted, then the settings screen that puts the operator's name and reply-to address on the packet, then the settings screen that sets the accent colour and logo on the scorecard.

Discovery, then the two settings screens that put your name and your colour on the document.

Five steps, in order

What it actually does

One operator, one business at a time. It automates the process I already ran by hand.

1

Find a business

Google Places API, one search area at a time.

2

Score the source

Six checks fetch the business's own pages and score the raw source. A summary of a page is never evidence.

3

Reconcile it yourself

You paste your own view-source and the check recomputes. A finding you do not reconcile cannot reach a document.

4

Generate the packet

A PDF scorecard, a schema starter kit, and delivery drafts.

5

Approve one at a time

Nothing auto-sends. src/main/send/provider.ts defines the outbound signature and nothing implements it.

Step three

You paste the source yourself

What you tell a prospect is that every finding in their document points at something they can see in their own source with Ctrl+U.

Generation refuses first. src/main/packet/generate.ts throws before any file is written if a finding has not been confirmed against source you pasted.

Confirmations expire after 72 hours. Past that, you re-paste.

Scores and severities are computed by code. Three inputs, and no others: 1. the raw source of the pages fetched 2. the Google Places listing fields (name, phone, address, primary type) 3. the date the check ran Five of the six checks then ask a model to reword the verdict the code already reached into a sentence an owner would understand. The model cannot change a severity, a count, a piece of evidence, or a fix. It gets no fetch tool. No browser. No search. Everything it writes is validated by src/main/checks/headline.ts before it can reach a document.
Where each one is enforced

The five laws

Three of the five also break a test or a type: law 2 in scripts/test-packet.js, law 3 in scripts/test-approval.js, law 4 at compile time.

#LawEnforced by
1AI-summarized page content is never evidence. Raw source only.src/main/evidence/fetch-raw.ts is the only network egress for target sites. The only other evidence is your own paste, wrapped by src/main/confirmation/gate.ts. Agents get no fetch tool, no browser and no search.
2Never fabricate a review, testimonial or result.src/main/packet/guardrails.ts, swept over every artifact before anything is written. It refuses rather than repairs.
3Nothing auto-sends, auto-posts or auto-prints. Approval is per item.src/main/approval/gate.ts registers every approved item in a module-private WeakSet at the moment it is minted, so assertMinted refuses anything that did not come through the gate. The unique symbol on the type is a compile-time brand and is erased at build; the runtime guarantee is the set.
4Every score prints its instrument and its base.The Score type at src/shared/types.ts has no optional fields, so omitting the base is a compile error.
5Discovery is the Places API plus the business's own site.A single discovery adapter. src/main/evidence/fetch-raw.ts refuses Google Maps and Search hosts, and private or loopback addresses, on every redirect hop.
From the README

Limitations

It does not observe AI search

Nothing here watches what an assistant actually says about a business. It measures what that business publishes for one to read.

The calibration set is two scans

Two delivered client scans, which between them publish three scored properties. scripts/test-instrument.js fails npm test if the instrument stops reproducing those three exactly.

Half of a listing finding is not yours

Where a finding compares the site against the Google listing, which is NAP consistency and booking path, only the site half is reconciled. The listing half is the app's own capture on both passes.

The AI-readiness score reconciles by band, not by number
Severity bands, src/main/checks/ai-readiness.ts · 105-point instrument, rescaled to 100
Reconciliation compares status and severity, not the exact figure. The widest band is 25 points wide. Bands are half-open, so a re-score of exactly 45 is severity 2.

Nothing counts until you confirm it.

Six checks · Nothing sends in this build

Go read it.

Every claim about how the tool behaves points at a file in the repo.

github.com/itsryanlenk/assay ▸

MIT licensed · Electron desktop app · Source only, no packaged build